careermate 0.0.4 → 0.0.6

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 (124) hide show
  1. package/AGENTS.md +11 -7
  2. package/INSTALL.md +18 -16
  3. package/README.md +28 -26
  4. package/THIRD_PARTY_NOTICES.md +2 -3
  5. package/bin/careermate.mjs +8 -1
  6. package/dist/career-os/README.md +8 -3
  7. package/dist/career-os/eop/career-description.md +136 -0
  8. package/dist/career-os/eop/cover-letter.md +7 -5
  9. package/dist/career-os/eop/fit-analysis.md +15 -5
  10. package/dist/career-os/eop/interview-prep.md +209 -21
  11. package/dist/career-os/eop/job-analysis.md +5 -3
  12. package/dist/career-os/eop/profile-extraction.md +5 -4
  13. package/dist/career-os/knowledge/cover-letter.md +4 -4
  14. package/dist/career-os/knowledge/fit-matching.md +8 -8
  15. package/dist/career-os/knowledge/interview-behavioral.md +76 -8
  16. package/dist/career-os/knowledge/interview-technical.md +106 -4
  17. package/dist/career-os/knowledge/linkedin-profile.md +1 -1
  18. package/dist/career-os/knowledge/networking-referrals.md +1 -1
  19. package/dist/career-os/knowledge/offer-evaluation-decision.md +10 -10
  20. package/dist/career-os/knowledge/portfolio.md +3 -3
  21. package/dist/career-os/knowledge/verifiers/ats-compat.md +8 -4
  22. package/dist/career-os/knowledge/verifiers/human-voice.md +2 -2
  23. package/dist/career-os/knowledge/verifiers/recency-staleness.md +1 -1
  24. package/dist/career-os/knowledge/verifiers/responsiveness-on-target.md +2 -2
  25. package/dist/career-os/knowledge/verifiers/truthfulness.md +1 -1
  26. package/dist/doctor.mjs +1019 -127
  27. package/dist/mcp.mjs +1036 -126
  28. package/dist/migrate.mjs +52 -1
  29. package/dist/public/app.js +42 -16
  30. package/dist/public/i18n/en/applications.js +23 -0
  31. package/dist/public/i18n/en/common.js +22 -0
  32. package/dist/public/i18n/en/documents.js +133 -0
  33. package/dist/public/i18n/en/home.js +75 -0
  34. package/dist/public/i18n/en/interview.js +69 -0
  35. package/dist/public/i18n/en/jobs.js +152 -0
  36. package/dist/public/i18n/en/nav.js +18 -0
  37. package/dist/public/i18n/en/profile.js +201 -0
  38. package/dist/public/i18n/en/settings.js +127 -0
  39. package/dist/public/i18n/en/status.js +22 -0
  40. package/dist/public/i18n/en.js +17 -0
  41. package/dist/public/i18n/ko/applications.js +23 -0
  42. package/dist/public/i18n/ko/common.js +22 -0
  43. package/dist/public/i18n/ko/documents.js +133 -0
  44. package/dist/public/i18n/ko/home.js +63 -0
  45. package/dist/public/i18n/ko/interview.js +69 -0
  46. package/dist/public/i18n/ko/jobs.js +152 -0
  47. package/dist/public/i18n/ko/nav.js +18 -0
  48. package/dist/public/i18n/ko/profile.js +201 -0
  49. package/dist/public/i18n/ko/settings.js +127 -0
  50. package/dist/public/i18n/ko/status.js +23 -0
  51. package/dist/public/i18n/ko.js +18 -0
  52. package/dist/public/i18n.js +55 -0
  53. package/dist/public/index.html +1 -0
  54. package/dist/public/lib.js +25 -17
  55. package/dist/public/pages/applications.js +17 -21
  56. package/dist/public/pages/documents.js +153 -107
  57. package/dist/public/pages/home.js +88 -53
  58. package/dist/public/pages/interview.js +105 -51
  59. package/dist/public/pages/jobs.js +330 -106
  60. package/dist/public/pages/profile.js +157 -152
  61. package/dist/public/pages/settings.js +270 -72
  62. package/dist/public/styles.css +38 -3
  63. package/dist/web.mjs +859 -103
  64. package/docs/FAQ.md +26 -5
  65. package/docs/MCP_TOOLS.md +26 -12
  66. package/docs/START_WORKFLOW.md +2 -2
  67. package/docs/SUPPORTED_AI_APPS.md +13 -0
  68. package/docs/WORKFLOWS.md +80 -23
  69. package/docs/career-os/README.md +8 -3
  70. package/docs/career-os/eop/career-description.md +136 -0
  71. package/docs/career-os/eop/cover-letter.md +7 -5
  72. package/docs/career-os/eop/fit-analysis.md +15 -5
  73. package/docs/career-os/eop/interview-prep.md +209 -21
  74. package/docs/career-os/eop/job-analysis.md +5 -3
  75. package/docs/career-os/eop/profile-extraction.md +5 -4
  76. package/docs/career-os/knowledge/cover-letter.md +4 -4
  77. package/docs/career-os/knowledge/fit-matching.md +8 -8
  78. package/docs/career-os/knowledge/interview-behavioral.md +76 -8
  79. package/docs/career-os/knowledge/interview-technical.md +106 -4
  80. package/docs/career-os/knowledge/linkedin-profile.md +1 -1
  81. package/docs/career-os/knowledge/networking-referrals.md +1 -1
  82. package/docs/career-os/knowledge/offer-evaluation-decision.md +10 -10
  83. package/docs/career-os/knowledge/portfolio.md +3 -3
  84. package/docs/career-os/knowledge/verifiers/ats-compat.md +8 -4
  85. package/docs/career-os/knowledge/verifiers/human-voice.md +2 -2
  86. package/docs/career-os/knowledge/verifiers/recency-staleness.md +1 -1
  87. package/docs/career-os/knowledge/verifiers/responsiveness-on-target.md +2 -2
  88. package/docs/career-os/knowledge/verifiers/truthfulness.md +1 -1
  89. package/package.json +2 -2
  90. package/packages/core/src/onboarding.ts +2 -1
  91. package/packages/core/src/services.ts +138 -21
  92. package/packages/core/src/summary.ts +59 -1
  93. package/packages/core/src/verify/lint.ts +4 -0
  94. package/packages/db/src/index.ts +1 -0
  95. package/packages/db/src/repositories.ts +0 -0
  96. package/packages/db/src/schema.ts +17 -0
  97. package/packages/exporters/src/index.ts +28 -17
  98. package/packages/knowledge/src/index.ts +46 -7
  99. package/packages/mcp-tools/src/bridge.ts +194 -13
  100. package/packages/mcp-tools/src/tools.ts +390 -38
  101. package/packages/mcp-tools/src/update.ts +31 -16
  102. package/packages/parsers/src/documents.ts +12 -1
  103. package/packages/prompts/src/humanize.ts +46 -0
  104. package/packages/prompts/src/index.ts +5 -1
  105. package/packages/prompts/src/install.ts +4 -4
  106. package/packages/prompts/src/onboarding.ts +4 -4
  107. package/packages/prompts/src/system.ts +17 -8
  108. package/packages/shared/src/enums.ts +14 -4
  109. package/packages/shared/src/schemas.ts +41 -0
  110. package/packages/workflows/src/definitions.ts +47 -10
  111. package/scripts/init.ts +284 -13
  112. package/dist/site/analytics.js +0 -97
  113. package/dist/site/favicon.svg +0 -1
  114. package/dist/site/fonts/PretendardVariable.woff2 +0 -0
  115. package/dist/site/icon-180.png +0 -0
  116. package/dist/site/icon-512.png +0 -0
  117. package/dist/site/index.html +0 -565
  118. package/dist/site/landing.js +0 -54
  119. package/dist/site/llms-install.txt +0 -218
  120. package/dist/site/robots.txt +0 -4
  121. package/dist/site/sitemap.xml +0 -21
  122. package/dist/site/start.html +0 -353
  123. package/dist/site/style.css +0 -559
  124. package/dist/site/vercel.json +0 -20
package/dist/web.mjs CHANGED
@@ -6746,15 +6746,15 @@ var require_generators = __commonJS({
6746
6746
  var stack = new Error().stack;
6747
6747
  return function() {
6748
6748
  var generator = generatorFunction.apply(this, arguments);
6749
- var spawn2 = new PromiseSpawn$(
6749
+ var spawn3 = new PromiseSpawn$(
6750
6750
  void 0,
6751
6751
  void 0,
6752
6752
  yieldHandler,
6753
6753
  stack
6754
6754
  );
6755
- var ret2 = spawn2.promise();
6756
- spawn2._generator = generator;
6757
- spawn2._promiseFulfilled(void 0);
6755
+ var ret2 = spawn3.promise();
6756
+ spawn3._generator = generator;
6757
+ spawn3._promiseFulfilled(void 0);
6758
6758
  return ret2;
6759
6759
  };
6760
6760
  };
@@ -6769,9 +6769,9 @@ var require_generators = __commonJS({
6769
6769
  if (typeof generatorFunction !== "function") {
6770
6770
  return apiRejection("generatorFunction must be a function\n\n See http://goo.gl/MqrFmX\n");
6771
6771
  }
6772
- var spawn2 = new PromiseSpawn(generatorFunction, this);
6773
- var ret2 = spawn2.promise();
6774
- spawn2._run(Promise2.spawn);
6772
+ var spawn3 = new PromiseSpawn(generatorFunction, this);
6773
+ var ret2 = spawn3.promise();
6774
+ spawn3._run(Promise2.spawn);
6775
6775
  return ret2;
6776
6776
  };
6777
6777
  };
@@ -28299,7 +28299,7 @@ var require_files = __commonJS({
28299
28299
  "use strict";
28300
28300
  var fs5 = __require("fs");
28301
28301
  var url = __require("url");
28302
- var os2 = __require("os");
28302
+ var os3 = __require("os");
28303
28303
  var dirname = __require("path").dirname;
28304
28304
  var resolvePath = __require("path").resolve;
28305
28305
  var isAbsolutePath = require_path_is_absolute();
@@ -28341,7 +28341,7 @@ var require_files = __commonJS({
28341
28341
  var readFile = promises.promisify(fs5.readFile.bind(fs5));
28342
28342
  function uriToPath(uriString, platform) {
28343
28343
  if (!platform) {
28344
- platform = os2.platform();
28344
+ platform = os3.platform();
28345
28345
  }
28346
28346
  var uri = url.parse(uriString);
28347
28347
  if (isLocalFileUri(uri) || isRelativeUri(uri)) {
@@ -39354,12 +39354,12 @@ var require_cjs = __commonJS({
39354
39354
  });
39355
39355
 
39356
39356
  // apps/web/src/index.ts
39357
- import { spawn } from "node:child_process";
39357
+ import { spawn as spawn2 } from "node:child_process";
39358
39358
 
39359
39359
  // apps/web/src/server.ts
39360
39360
  import http from "node:http";
39361
39361
  import path5 from "node:path";
39362
- import { fileURLToPath } from "node:url";
39362
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
39363
39363
 
39364
39364
  // packages/db/src/paths.ts
39365
39365
  import os from "node:os";
@@ -39681,6 +39681,22 @@ var MIGRATIONS = [
39681
39681
 
39682
39682
  COMMIT;
39683
39683
  PRAGMA foreign_keys=ON;
39684
+ `,
39685
+ // v3 — per-job timeline entries. Document/cover-letter refs live in payload
39686
+ // snapshots instead of FKs so deleting a document does not erase the history.
39687
+ `
39688
+ CREATE TABLE IF NOT EXISTS application_timeline_events (
39689
+ id TEXT PRIMARY KEY,
39690
+ job_id TEXT NOT NULL REFERENCES jobs(id) ON DELETE CASCADE,
39691
+ type TEXT NOT NULL,
39692
+ title TEXT NOT NULL,
39693
+ summary TEXT,
39694
+ payload TEXT NOT NULL DEFAULT '{}',
39695
+ occurred_at TEXT NOT NULL,
39696
+ created_at TEXT NOT NULL
39697
+ );
39698
+
39699
+ CREATE INDEX IF NOT EXISTS idx_timeline_job ON application_timeline_events(job_id, occurred_at ASC);
39684
39700
  `
39685
39701
  ];
39686
39702
  function migrate(db) {
@@ -39869,6 +39885,13 @@ var ACTIVITY_TYPES = [
39869
39885
  "interview_prep_saved",
39870
39886
  "document_exported"
39871
39887
  ];
39888
+ var APPLICATION_TIMELINE_TYPES = [
39889
+ "job_saved",
39890
+ "fit_analysis_saved",
39891
+ "cover_letter_version_saved",
39892
+ "application_status_changed",
39893
+ "interview_prep_saved"
39894
+ ];
39872
39895
  var ENTITY_TYPES = [
39873
39896
  "profile",
39874
39897
  "experience",
@@ -44176,11 +44199,19 @@ var ApplicationInputSchema = external_exports.object({
44176
44199
  applied_at: optLine,
44177
44200
  notes: optNote
44178
44201
  });
44202
+ var ApplicationSubmissionSchema = external_exports.object({
44203
+ submitted_at: optLine.describe("\uC2E4\uC81C \uC81C\uCD9C\uC77C YYYY-MM-DD"),
44204
+ channel: optLine.describe("\uC81C\uCD9C \uCC44\uB110(\uC6D0\uD2F0\uB4DC/\uC0AC\uB78C\uC778/\uD68C\uC0AC \uCC44\uC6A9\uD398\uC774\uC9C0 \uB4F1)"),
44205
+ cover_letter_id: optLine,
44206
+ cover_letter_version_id: optLine,
44207
+ 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")
44208
+ });
44179
44209
  var ApplicationStatusUpdateSchema = external_exports.object({
44180
44210
  job_id: optLine,
44181
44211
  application_id: optLine,
44182
44212
  status: external_exports.enum(APPLICATION_STATUSES),
44183
- note: optNote.describe("\uC0C1\uD0DC \uBCC0\uACBD \uC0AC\uC720/\uBA54\uBAA8")
44213
+ note: optNote.describe("\uC0C1\uD0DC \uBCC0\uACBD \uC0AC\uC720/\uBA54\uBAA8"),
44214
+ submission: ApplicationSubmissionSchema.optional().describe("\uC9C0\uC6D0 \uC644\uB8CC \uAE30\uB85D\uC5D0 \uB0A8\uAE38 \uC81C\uCD9C \uC790\uB8CC \uC2A4\uB0C5\uC0F7")
44184
44215
  });
44185
44216
  var ApplicationRecordSchema = external_exports.object({
44186
44217
  ...baseRecord,
@@ -44198,8 +44229,10 @@ var StarGuideSchema = external_exports.object({
44198
44229
  action: optNote,
44199
44230
  result: optNote
44200
44231
  });
44232
+ var INTERVIEW_CATEGORIES = ["technical", "behavioral"];
44201
44233
  var InterviewQuestionSchema = external_exports.object({
44202
44234
  question: external_exports.string().max(MAX_NOTE),
44235
+ 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."),
44203
44236
  intent: external_exports.string().max(MAX_NOTE).optional().describe("\uC9C8\uBB38 \uC758\uB3C4"),
44204
44237
  followups: strList.optional().describe("\uC608\uC0C1 \uAF2C\uB9AC \uC9C8\uBB38"),
44205
44238
  answer_outline: optBody.describe("\uB2F5\uBCC0 \uAC00\uC774\uB4DC/\uD575\uC2EC \uD3EC\uC778\uD2B8")
@@ -44227,6 +44260,24 @@ var ActivityRecordSchema = external_exports.object({
44227
44260
  summary: external_exports.string(),
44228
44261
  created_at: isoDateTime
44229
44262
  });
44263
+ var ApplicationTimelineInputSchema = external_exports.object({
44264
+ job_id: reqLine,
44265
+ type: external_exports.enum(APPLICATION_TIMELINE_TYPES),
44266
+ title: reqLine,
44267
+ summary: optNote,
44268
+ payload: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
44269
+ occurred_at: optLine
44270
+ });
44271
+ var ApplicationTimelineRecordSchema = external_exports.object({
44272
+ id,
44273
+ job_id: external_exports.string(),
44274
+ type: external_exports.enum(APPLICATION_TIMELINE_TYPES),
44275
+ title: external_exports.string(),
44276
+ summary: external_exports.string().nullable(),
44277
+ payload: external_exports.record(external_exports.string(), external_exports.unknown()),
44278
+ occurred_at: isoDateTime,
44279
+ created_at: isoDateTime
44280
+ });
44230
44281
  var OnboardingStatusSchema = external_exports.object({
44231
44282
  completed: external_exports.boolean(),
44232
44283
  has_profile: external_exports.boolean(),
@@ -44263,7 +44314,7 @@ var ApplicationContextSchema = external_exports.object({
44263
44314
  });
44264
44315
 
44265
44316
  // packages/shared/src/build-info.ts
44266
- var APP_VERSION = true ? "0.0.4" : "0.0.0-dev";
44317
+ var APP_VERSION = true ? "0.0.6" : "0.0.0-dev";
44267
44318
  var BUNDLED = true;
44268
44319
 
44269
44320
  // packages/shared/src/index.ts
@@ -44357,7 +44408,7 @@ function mapExperience(r) {
44357
44408
  };
44358
44409
  }
44359
44410
  function expKey(company, role, start) {
44360
- return [norm(company), norm(role), norm(start)].join("\0");
44411
+ return [norm(company), norm(role), norm(start)].join("");
44361
44412
  }
44362
44413
  var experienceRepo = {
44363
44414
  list() {
@@ -44775,6 +44826,7 @@ var coverLetterRepo = {
44775
44826
  remove(id3) {
44776
44827
  const db = getDb();
44777
44828
  return tx(() => {
44829
+ db.prepare(`UPDATE applications SET cover_letter_id=NULL, updated_at=? WHERE cover_letter_id=?`).run(now(), id3);
44778
44830
  db.prepare(`DELETE FROM cover_letter_versions WHERE cover_letter_id=?`).run(id3);
44779
44831
  return db.prepare(`DELETE FROM cover_letters WHERE id=?`).run(id3).changes > 0;
44780
44832
  });
@@ -44863,9 +44915,11 @@ var jobRepo = {
44863
44915
  remove(id3) {
44864
44916
  const db = getDb();
44865
44917
  return tx(() => {
44918
+ db.prepare(`UPDATE cover_letters SET job_id=NULL, updated_at=? WHERE job_id=?`).run(now(), id3);
44866
44919
  db.prepare(`DELETE FROM fit_analyses WHERE job_id=?`).run(id3);
44867
44920
  db.prepare(`DELETE FROM applications WHERE job_id=?`).run(id3);
44868
44921
  db.prepare(`DELETE FROM interview_preps WHERE job_id=?`).run(id3);
44922
+ db.prepare(`DELETE FROM application_timeline_events WHERE job_id=?`).run(id3);
44869
44923
  return db.prepare(`DELETE FROM jobs WHERE id=?`).run(id3).changes > 0;
44870
44924
  });
44871
44925
  }
@@ -44978,7 +45032,7 @@ var applicationRepo = {
44978
45032
  const m = { ...app, ...input };
44979
45033
  getDb().prepare(
44980
45034
  `UPDATE applications SET status=?,resume_id=?,cover_letter_id=?,applied_at=?,notes=?,updated_at=? WHERE id=?`
44981
- ).run(m.status, m.resume_id, m.cover_letter_id, m.applied_at, m.notes, now(), app.id);
45035
+ ).run(m.status, m.resume_id ?? null, m.cover_letter_id ?? null, m.applied_at ?? null, m.notes ?? null, now(), app.id);
44982
45036
  return this.get(app.id);
44983
45037
  },
44984
45038
  setStatus(jobId, status, note) {
@@ -45059,6 +45113,48 @@ var activityRepo = {
45059
45113
  return getDb().prepare(`SELECT * FROM activities ORDER BY created_at DESC LIMIT ?`).all(limit);
45060
45114
  }
45061
45115
  };
45116
+ function mapTimelineEvent(r) {
45117
+ return {
45118
+ id: r.id,
45119
+ job_id: r.job_id,
45120
+ type: r.type,
45121
+ title: r.title,
45122
+ summary: r.summary,
45123
+ payload: fromJson(r.payload, {}),
45124
+ occurred_at: r.occurred_at,
45125
+ created_at: r.created_at
45126
+ };
45127
+ }
45128
+ var timelineRepo = {
45129
+ add(input) {
45130
+ const ts = now();
45131
+ const id3 = newId("tl_");
45132
+ getDb().prepare(
45133
+ `INSERT INTO application_timeline_events (id,job_id,type,title,summary,payload,occurred_at,created_at)
45134
+ VALUES (?,?,?,?,?,?,?,?)`
45135
+ ).run(
45136
+ id3,
45137
+ input.job_id,
45138
+ input.type,
45139
+ input.title,
45140
+ input.summary ?? null,
45141
+ toJson(input.payload ?? {}),
45142
+ input.occurred_at ?? ts,
45143
+ ts
45144
+ );
45145
+ return this.get(id3);
45146
+ },
45147
+ get(id3) {
45148
+ const r = getDb().prepare(`SELECT * FROM application_timeline_events WHERE id=?`).get(id3);
45149
+ return r ? mapTimelineEvent(r) : null;
45150
+ },
45151
+ listByJob(jobId) {
45152
+ return getDb().prepare(`SELECT * FROM application_timeline_events WHERE job_id=? ORDER BY occurred_at ASC, created_at ASC`).all(jobId).map(mapTimelineEvent);
45153
+ },
45154
+ remove(id3) {
45155
+ return getDb().prepare(`DELETE FROM application_timeline_events WHERE id=?`).run(id3).changes > 0;
45156
+ }
45157
+ };
45062
45158
  function getEntityCounts() {
45063
45159
  const db = getDb();
45064
45160
  const ALLOWED_TABLES = /* @__PURE__ */ new Set([
@@ -45070,7 +45166,8 @@ function getEntityCounts() {
45070
45166
  "cover_letters",
45071
45167
  "jobs",
45072
45168
  "applications",
45073
- "interview_preps"
45169
+ "interview_preps",
45170
+ "application_timeline_events"
45074
45171
  ]);
45075
45172
  const c = (t) => {
45076
45173
  if (!ALLOWED_TABLES.has(t)) throw new Error(`getEntityCounts: \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uC740 \uD14C\uC774\uBE14 \uC774\uB984: ${t}`);
@@ -45085,7 +45182,8 @@ function getEntityCounts() {
45085
45182
  cover_letters: c("cover_letters"),
45086
45183
  jobs: c("jobs"),
45087
45184
  applications: c("applications"),
45088
- interview_preps: c("interview_preps")
45185
+ interview_preps: c("interview_preps"),
45186
+ application_timeline_events: c("application_timeline_events")
45089
45187
  };
45090
45188
  }
45091
45189
 
@@ -45151,7 +45249,7 @@ function injectToken(html) {
45151
45249
  // apps/web/src/http.ts
45152
45250
  import fs3 from "node:fs";
45153
45251
  import path3 from "node:path";
45154
- var MAX_BODY2 = 8 * 1024 * 1024;
45252
+ var MAX_BODY2 = 32 * 1024 * 1024;
45155
45253
  var Router = class {
45156
45254
  routes = [];
45157
45255
  add(method, path6, handler) {
@@ -45330,12 +45428,13 @@ function profileCompleteness(p) {
45330
45428
  function getOnboardingStatus() {
45331
45429
  const profile = profileRepo.get();
45332
45430
  const resumes = documentRepo.list("resume");
45431
+ const careerDescriptions = documentRepo.list("career_description");
45333
45432
  const coverLetters = coverLetterRepo.list();
45334
45433
  const experiences = experienceRepo.list();
45335
45434
  const skills = skillRepo.list();
45336
45435
  const jobs = jobRepo.list();
45337
45436
  const has_profile = !!(profile && profile.name);
45338
- const has_resume = resumes.length > 0;
45437
+ const has_resume = resumes.length > 0 || careerDescriptions.length > 0;
45339
45438
  const has_cover_letter = coverLetters.length > 0;
45340
45439
  const has_experience = experiences.length > 0;
45341
45440
  const has_skills = skills.length > 0;
@@ -45738,6 +45837,29 @@ function addSkill(input) {
45738
45837
  activityRepo.log("profile_updated", `\uAE30\uC220(${skill.name})\uC744 \uCD94\uAC00\uD588\uC2B5\uB2C8\uB2E4.`, "skill", skill.id);
45739
45838
  return skill;
45740
45839
  }
45840
+ function coverLetterTimelineRef(coverLetter, version, requestedVersionId) {
45841
+ const resolvedVersion = version ?? coverLetter.versions?.find((v) => v.id === requestedVersionId || v.id === coverLetter.current_version_id) ?? null;
45842
+ return {
45843
+ kind: "cover_letter",
45844
+ id: coverLetter.id,
45845
+ title: coverLetter.title,
45846
+ version_id: resolvedVersion?.id ?? requestedVersionId ?? coverLetter.current_version_id,
45847
+ version_no: resolvedVersion?.version_no ?? coverLetter.version_count
45848
+ };
45849
+ }
45850
+ function documentTimelineRef(doc) {
45851
+ return {
45852
+ kind: "document",
45853
+ id: doc.id,
45854
+ title: doc.title,
45855
+ document_kind: doc.kind
45856
+ };
45857
+ }
45858
+ function submittedAtToOccurrence(value) {
45859
+ if (!value) return void 0;
45860
+ if (/^\d{4}-\d{2}-\d{2}$/.test(value)) return `${value}T00:00:00.000Z`;
45861
+ return void 0;
45862
+ }
45741
45863
  function saveJobPosting(input, id3) {
45742
45864
  const job = jobRepo.upsert(input, id3);
45743
45865
  const application = applicationRepo.ensure(job.id, "draft");
@@ -45746,11 +45868,22 @@ function saveJobPosting(input, id3) {
45746
45868
  }
45747
45869
  function saveFitAnalysis(input) {
45748
45870
  const job = jobRepo.get(input.job_id);
45749
- if (!job) throw notFound(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.job_id}. \uBA3C\uC800 save_job_posting\uC73C\uB85C \uACF5\uACE0\uB97C \uC800\uC7A5\uD558\uC138\uC694.`);
45871
+ if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 \uACF5\uACE0\uB97C \uC800\uC7A5\uD55C \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.");
45750
45872
  applicationRepo.ensure(job.id, "draft");
45751
45873
  const fit = fitRepo.save(input);
45752
45874
  const scoreText = fit.score != null ? ` (\uC801\uD569\uB3C4 ${fit.score}\uC810)` : "";
45753
45875
  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);
45876
+ timelineRepo.add({
45877
+ job_id: job.id,
45878
+ type: "fit_analysis_saved",
45879
+ title: "\uC801\uD569\uB3C4 \uBD84\uC11D",
45880
+ summary: fit.score != null ? `\uC885\uD569 ${fit.score}\uC810` : fit.summary ?? void 0,
45881
+ payload: {
45882
+ fit_id: fit.id,
45883
+ score: fit.score
45884
+ },
45885
+ occurred_at: fit.updated_at
45886
+ });
45754
45887
  return fit;
45755
45888
  }
45756
45889
  function saveCoverLetterVersion(input) {
@@ -45784,51 +45917,123 @@ function saveCoverLetterVersion(input) {
45784
45917
  "cover_letter",
45785
45918
  coverLetter.id
45786
45919
  );
45920
+ if (coverLetter.job_id) {
45921
+ timelineRepo.add({
45922
+ job_id: coverLetter.job_id,
45923
+ type: "cover_letter_version_saved",
45924
+ title: "\uC790\uAE30\uC18C\uAC1C\uC11C \uC791\uC131",
45925
+ summary: `${coverLetter.title} v${version.version_no}`,
45926
+ payload: {
45927
+ cover_letter: coverLetterTimelineRef(coverLetter, version)
45928
+ },
45929
+ occurred_at: version.created_at
45930
+ });
45931
+ }
45787
45932
  return { coverLetter, version, verification, forced };
45788
45933
  }
45789
- function updateApplicationStatus(jobId, status, note) {
45934
+ function updateApplicationStatus(jobId, status, note, submission) {
45790
45935
  const job = jobRepo.get(jobId);
45791
- if (!job) throw notFound(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${jobId}`);
45792
- const application = applicationRepo.setStatus(jobId, status, note);
45793
- activityRepo.log(
45794
- "application_status_changed",
45795
- `${job.company} \xB7 ${job.position} \uC0C1\uD0DC\uB97C '${APPLICATION_STATUS_LABELS[status]}'(\uC73C)\uB85C \uBCC0\uACBD\uD588\uC2B5\uB2C8\uB2E4.`,
45796
- "application",
45797
- application.id
45798
- );
45936
+ if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
45937
+ const before2 = applicationRepo.getByJob(jobId);
45938
+ const application = tx(() => {
45939
+ let application2 = applicationRepo.setStatus(jobId, status, note);
45940
+ const submittedDocs = (submission?.document_ids ?? []).map((docId) => documentRepo.get(docId)).filter((doc) => !!doc);
45941
+ const submittedCoverLetter = submission?.cover_letter_id ? coverLetterRepo.get(submission.cover_letter_id, true) : null;
45942
+ if (status === "applied" && submission) {
45943
+ const resumeLike = submittedDocs.find((doc) => doc.kind === "resume" || doc.kind === "career_description") ?? submittedDocs[0] ?? null;
45944
+ application2 = applicationRepo.upsert({
45945
+ job_id: jobId,
45946
+ resume_id: resumeLike?.id ?? application2.resume_id ?? void 0,
45947
+ cover_letter_id: submittedCoverLetter?.id ?? application2.cover_letter_id ?? void 0,
45948
+ applied_at: submission.submitted_at ?? application2.applied_at ?? void 0
45949
+ });
45950
+ }
45951
+ activityRepo.log(
45952
+ "application_status_changed",
45953
+ `${job.company} \xB7 ${job.position} \uC0C1\uD0DC\uB97C '${APPLICATION_STATUS_LABELS[status]}'(\uC73C)\uB85C \uBCC0\uACBD\uD588\uC2B5\uB2C8\uB2E4.`,
45954
+ "application",
45955
+ application2.id
45956
+ );
45957
+ const submittedAt = submission?.submitted_at?.trim();
45958
+ const channel = submission?.channel?.trim();
45959
+ const submissionPayload = submission && (submittedAt || channel || submittedCoverLetter || submittedDocs.length > 0) ? {
45960
+ submitted_at: submittedAt || null,
45961
+ channel: channel || null,
45962
+ cover_letter: submittedCoverLetter ? coverLetterTimelineRef(submittedCoverLetter, null, submission.cover_letter_version_id) : null,
45963
+ documents: submittedDocs.map(documentTimelineRef)
45964
+ } : null;
45965
+ timelineRepo.add({
45966
+ job_id: job.id,
45967
+ type: "application_status_changed",
45968
+ title: APPLICATION_STATUS_LABELS[status],
45969
+ summary: channel && status === "applied" ? `${channel}\uB85C \uC81C\uCD9C` : note ?? void 0,
45970
+ payload: {
45971
+ from_status: before2?.status ?? null,
45972
+ status,
45973
+ status_label: APPLICATION_STATUS_LABELS[status],
45974
+ note: note ?? null,
45975
+ submission: submissionPayload
45976
+ },
45977
+ occurred_at: submittedAtToOccurrence(submittedAt)
45978
+ });
45979
+ return application2;
45980
+ });
45799
45981
  const interview_unlocked = INTERVIEW_UNLOCK_STATUSES.includes(status);
45800
45982
  const hasPrep = !!interviewRepo.getByJob(jobId);
45801
- const hint = interview_unlocked && !hasPrep ? "\uC11C\uB958 \uB2E8\uACC4\uB97C \uD1B5\uACFC\uD588\uC5B4\uC694. \uC774 \uACF5\uACE0 \uAE30\uC900\uC73C\uB85C \uC608\uC0C1 \uBA74\uC811 \uC9C8\uBB38\uACFC 1\uBD84 \uC790\uAE30\uC18C\uAC1C\uB97C \uC900\uBE44\uD560\uAE4C\uC694? (save_interview_prep)" : null;
45983
+ 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;
45802
45984
  return { application, job, interview_unlocked, hint };
45803
45985
  }
45804
45986
  function saveInterviewPrep(input) {
45805
45987
  const job = jobRepo.get(input.job_id);
45806
- if (!job) throw notFound(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.job_id}`);
45807
- const app = applicationRepo.getByJob(job.id);
45808
- if (!app || !INTERVIEW_UNLOCK_STATUSES.includes(app.status)) {
45809
- const current = app ? APPLICATION_STATUS_LABELS[app.status] : "\uBBF8\uC9C0\uC6D0";
45810
- throw conflict(
45811
- `\uBA74\uC811 \uC900\uBE44\uB294 '\uC11C\uB958 \uD569\uACA9' \uC774\uC0C1 \uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uC800\uC7A5\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uD604\uC7AC \uC0C1\uD0DC: '${current}'. \uBA3C\uC800 update_application_status\uB85C \uC0C1\uD0DC\uB97C \uC62C\uB9B0 \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.`
45812
- );
45813
- }
45988
+ if (!job) throw notFound("\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
45814
45989
  const prep = interviewRepo.save(input);
45815
45990
  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);
45991
+ timelineRepo.add({
45992
+ job_id: job.id,
45993
+ type: "interview_prep_saved",
45994
+ title: "\uBA74\uC811 \uC900\uBE44",
45995
+ summary: prep.questions.length > 0 ? `\uC608\uC0C1 \uC9C8\uBB38 ${prep.questions.length}\uAC1C` : prep.notes ?? void 0,
45996
+ payload: {
45997
+ interview_prep_id: prep.id,
45998
+ question_count: prep.questions.length
45999
+ },
46000
+ occurred_at: prep.updated_at
46001
+ });
45816
46002
  return prep;
45817
46003
  }
45818
46004
 
45819
46005
  // packages/core/src/summary.ts
45820
46006
  var ACTIVE = ["draft", "planned", "applied", "document_passed", "interview"];
46007
+ var DEADLINE_ACTIONABLE = ["draft", "planned"];
46008
+ var DEADLINE_SOON_DAYS = 14;
46009
+ var FOLLOWUP_STALE_DAYS = 10;
46010
+ var DAY_MS = 864e5;
46011
+ function daysUntilDate(dateStr, now2) {
46012
+ if (!dateStr) return null;
46013
+ const d = /* @__PURE__ */ new Date(`${dateStr}T00:00:00`);
46014
+ if (isNaN(d.getTime())) return null;
46015
+ const today = new Date(now2);
46016
+ today.setHours(0, 0, 0, 0);
46017
+ return Math.round((d.getTime() - today.getTime()) / DAY_MS);
46018
+ }
46019
+ function daysSinceTimestamp(iso, now2) {
46020
+ if (!iso) return null;
46021
+ const d = new Date(iso);
46022
+ if (isNaN(d.getTime())) return null;
46023
+ return Math.floor((now2.getTime() - d.getTime()) / DAY_MS);
46024
+ }
45821
46025
  function jobWithMeta(job) {
45822
46026
  const app = applicationRepo.getByJob(job.id);
45823
46027
  const fit = fitRepo.getByJob(job.id);
45824
46028
  const status = app?.status ?? "draft";
45825
46029
  return { ...job, status, status_label: APPLICATION_STATUS_LABELS[status], fit_score: fit?.score ?? null };
45826
46030
  }
45827
- function getHomeSummary() {
46031
+ function getHomeSummary(now2 = /* @__PURE__ */ new Date()) {
45828
46032
  const onboarding = getOnboardingStatus();
45829
46033
  const jobs = jobRepo.list();
45830
46034
  const applications = applicationRepo.list();
45831
46035
  const coverLetters = coverLetterRepo.list();
46036
+ const statusByJob = new Map(applications.map((a) => [a.job_id, a]));
45832
46037
  const status_breakdown = Object.keys(APPLICATION_STATUS_LABELS).map((status) => ({
45833
46038
  status,
45834
46039
  label: APPLICATION_STATUS_LABELS[status],
@@ -45839,6 +46044,19 @@ function getHomeSummary() {
45839
46044
  const job = jobRepo.get(a.job_id);
45840
46045
  return job ? { job, status_label: APPLICATION_STATUS_LABELS[a.status] } : null;
45841
46046
  }).filter((x) => x !== null);
46047
+ const deadlines = jobs.map((job) => {
46048
+ const status = statusByJob.get(job.id)?.status ?? "draft";
46049
+ if (!DEADLINE_ACTIONABLE.includes(status)) return null;
46050
+ const days_left = daysUntilDate(job.deadline, now2);
46051
+ if (days_left == null || days_left > DEADLINE_SOON_DAYS) return null;
46052
+ return { job, status, status_label: APPLICATION_STATUS_LABELS[status], days_left };
46053
+ }).filter((x) => x !== null).sort((a, b) => a.days_left - b.days_left).slice(0, 5);
46054
+ const followups = applications.filter((a) => a.status === "applied").map((a) => {
46055
+ const days_since = daysSinceTimestamp(a.applied_at, now2);
46056
+ if (days_since == null || days_since < FOLLOWUP_STALE_DAYS) return null;
46057
+ const job = jobRepo.get(a.job_id);
46058
+ return job ? { job, days_since } : null;
46059
+ }).filter((x) => x !== null).sort((a, b) => b.days_since - a.days_since).slice(0, 5);
45842
46060
  return {
45843
46061
  onboarding,
45844
46062
  counts: {
@@ -45859,6 +46077,8 @@ function getHomeSummary() {
45859
46077
  }),
45860
46078
  recent_cover_letters: coverLetters.slice(0, 5),
45861
46079
  interview_todo,
46080
+ deadlines,
46081
+ followups,
45862
46082
  recent_activity: activityRepo.recent(10)
45863
46083
  };
45864
46084
  }
@@ -45879,7 +46099,7 @@ CareerMate\uB294 \uC0AC\uC6A9\uC790\uC758 \uCEF4\uD4E8\uD130\uC5D0\uC11C \uB3D9\
45879
46099
  - \uAE30\uC5B5\uC5D0 \uC758\uC874\uD558\uC9C0 \uB9D0\uACE0, \uB9E4 \uC791\uC5C5 \uC2DC\uC791 \uC2DC \uC2E4\uC81C \uB370\uC774\uD130\uB97C \uB2E4\uC2DC \uBD88\uB7EC\uC624\uC138\uC694.
45880
46100
 
45881
46101
  2. \uACB0\uACFC\uB294 \uBC18\uB4DC\uC2DC \uB2E4\uC2DC \uC800\uC7A5\uD569\uB2C8\uB2E4.
45882
- - \uBD84\uC11D\xB7\uC791\uC131\uD55C \uB0B4\uC6A9\uC740 \uBA38\uB9BF\uC18D\uC774\uB098 \uB300\uD654\uCC3D\uC5D0\uB9CC \uB0A8\uAE30\uC9C0 \uB9D0\uACE0 \uD574\uB2F9 \uC800\uC7A5 \uD234\uB85C CareerMate\uC5D0 \uAE30\uB85D\uD558\uC138\uC694: \uACF5\uACE0\uB294 \`save_job_posting\`, \uC801\uD569\uB3C4 \uBD84\uC11D\uC740 \`save_fit_analysis\`, \uC790\uAE30\uC18C\uAC1C\uC11C \uBC84\uC804\uC740 \`save_cover_letter_version\`, \uBA74\uC811 \uC900\uBE44\uB294 \`save_interview_prep\`, \uC0C1\uD0DC \uBCC0\uACBD\uC740 \`update_application_status\`.
46102
+ - \uBD84\uC11D\xB7\uC791\uC131\uD55C \uB0B4\uC6A9\uC740 \uBA38\uB9BF\uC18D\uC774\uB098 \uB300\uD654\uCC3D\uC5D0\uB9CC \uB0A8\uAE30\uC9C0 \uB9D0\uACE0 \uD574\uB2F9 \uC800\uC7A5 \uD234\uB85C CareerMate\uC5D0 \uAE30\uB85D\uD558\uC138\uC694: \uACF5\uACE0\uB294 \`save_job_posting\`, \uC801\uD569\uB3C4 \uBD84\uC11D\uC740 \`save_fit_analysis\`, \uC790\uAE30\uC18C\uAC1C\uC11C \uBC84\uC804\uC740 \`save_cover_letter_version\`, \uACBD\uB825\uAE30\uC220\uC11C\uB294 \`add_resume\`(kind=career_description), \uBA74\uC811 \uC900\uBE44\uB294 \`save_interview_prep\`, \uC0C1\uD0DC \uBCC0\uACBD\uC740 \`update_application_status\`.
45883
46103
  - \uC800\uC7A5 \uD6C4\uC5D0\uB294 \uBB34\uC5C7\uC774 \uC800\uC7A5\uB418\uC5C8\uB294\uC9C0 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uAC04\uB2E8\uD788 \uC54C\uB824\uC8FC\uC138\uC694.
45884
46104
 
45885
46105
  3. \uB370\uC774\uD130\uB97C \uC9C0\uC5B4\uB0B4\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.
@@ -45891,23 +46111,32 @@ CareerMate\uB294 \uC0AC\uC6A9\uC790\uC758 \uCEF4\uD4E8\uD130\uC5D0\uC11C \uB3D9\
45891
46111
  - \uBBFC\uAC10 \uC815\uBCF4(\uC5F0\uB77D\uCC98, \uC8FC\uC18C \uB4F1)\uB294 \uAF2D \uD544\uC694\uD55C \uC791\uC5C5\uC5D0\uB9CC \uC0AC\uC6A9\uD558\uACE0 \uBD88\uD544\uC694\uD558\uAC8C \uB178\uCD9C\uD558\uC9C0 \uB9C8\uC138\uC694.
45892
46112
 
45893
46113
  5. \uC790\uAE30\uC18C\uAC1C\uC11C\uB294 \uC791\uC131 \uC804 \uD655\uC778\uC744 \uBC1B\uC2B5\uB2C8\uB2E4.
45894
- - \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC0C8\uB85C \uC4F0\uAC70\uB098 \uD06C\uAC8C \uACE0\uCE58\uAE30 \uC804\uC5D0, \uC5B4\uB5A4 \uACF5\uACE0\uB97C \uB300\uC0C1\uC73C\uB85C \uC5B4\uB5A4 \uD1A4\uACFC \uAC15\uC870\uC810\uC73C\uB85C \uC4F8\uC9C0 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694.
46114
+ - \uC790\uAE30\uC18C\uAC1C\uC11C\uB098 \uACBD\uB825\uAE30\uC220\uC11C\uB97C \uC0C8\uB85C \uC4F0\uAC70\uB098 \uD06C\uAC8C \uACE0\uCE58\uAE30 \uC804\uC5D0, \uC5B4\uB5A4 \uACF5\uACE0/\uC9C1\uBB34\uB97C \uB300\uC0C1\uC73C\uB85C \uC5B4\uB5A4 \uD1A4\uACFC \uAC15\uC870\uC810\uC73C\uB85C \uC4F8\uC9C0 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694.
45895
46115
  - \uC791\uC131 \uD6C4\uC5D0\uB294 \`save_cover_letter_version\`\uC73C\uB85C \uBC84\uC804\uC744 \uC800\uC7A5\uD558\uACE0, \uBCC0\uACBD \uC694\uC57D\uC744 \`note\`\uC5D0 \uB0A8\uAE30\uC138\uC694. \uC0AC\uC6A9\uC790\uAC00 \uC6D0\uD558\uBA74 \`export_cover_letter\`\uB85C \uB0B4\uBCF4\uB0B4\uC138\uC694.
45896
46116
 
45897
- 6. \uC0AC\uC6A9\uC790\uC5D0\uAC8C\uB294 \uC26C\uC6B4 \uD55C\uAD6D\uC5B4\uB85C \uC54C\uB9BD\uB2C8\uB2E4.
46117
+ 6. \uC0AC\uC6A9\uC790\uC5D0\uAC8C\uB294 \uC0AC\uC6A9\uC790\uC758 \uC5B8\uC5B4\uB85C \uC27D\uAC8C \uC54C\uB9BD\uB2C8\uB2E4.
46118
+ - **\uC751\uB2F5 \uC5B8\uC5B4 = \uC0AC\uC6A9\uC790\uC758 \uC5B8\uC5B4.** \uC0AC\uC6A9\uC790\uAC00 \uD55C\uAD6D\uC5B4\uB85C \uB9D0\uD558\uBA74 \uD55C\uAD6D\uC5B4\uB85C, \uC601\uC5B4 \uB4F1 \uB2E4\uB978 \uC5B8\uC5B4\uB85C \uB9D0\uD558\uBA74 \uADF8 \uC5B8\uC5B4\uB85C \uB2F5\uD569\uB2C8\uB2E4. \uC774 \uC9C0\uCE68\xB7\uB3C4\uAD6C \uC751\uB2F5\xB7\uD50C\uB808\uC774\uBD81\uC774 \uD55C\uAD6D\uC5B4\uB85C \uC801\uD600 \uC788\uC5B4\uB3C4 \uADF8\uB300\uB85C \uBCF5\uC0AC\uD558\uC9C0 \uB9D0\uACE0 \uC0AC\uC6A9\uC790 \uC5B8\uC5B4\uB85C \uC62E\uACA8 \uC804\uB2EC\uD558\uC138\uC694(\uC0AC\uC2E4\xB7\uC218\uCE58\xB7\uACE0\uC720\uBA85\uC0AC\uB294 \uBCF4\uC874).
45898
46119
  - "MCP", "\uD234 \uD638\uCD9C", "job_id" \uAC19\uC740 \uAE30\uC220 \uC6A9\uC5B4\uB294 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC9C1\uC811 \uB178\uCD9C\uD558\uC9C0 \uB9C8\uC138\uC694. "\uACF5\uACE0\uB97C \uC800\uC7A5\uD588\uC5B4\uC694", "\uC801\uD569\uB3C4\uB97C \uBD84\uC11D\uD588\uC5B4\uC694"\uCC98\uB7FC \uC77C\uC0C1\uC801\uC778 \uD45C\uD604\uC73C\uB85C \uC9C4\uD589 \uC0C1\uD669\uC744 \uC54C\uB9AC\uC138\uC694.
46120
+ - \uB3C4\uAD6C \uC751\uB2F5\uC5D0 \`user_message\`\uAC00 \uC788\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBCF4\uACE0\uD560 \uB54C \uADF8 \uB0B4\uC6A9\uC744 \uC6B0\uC120 \uD65C\uC6A9\uD558\uB418, \uC0AC\uC6A9\uC790\uAC00 \uD55C\uAD6D\uC5B4\uAC00 \uC544\uB2CC \uC5B8\uC5B4\uB85C \uB300\uD654\uD558\uBA74 \uD55C\uAD6D\uC5B4 \uC6D0\uBB38\uC744 \uADF8\uB300\uB85C \uBCF5\uC0AC\uD558\uC9C0 \uB9D0\uACE0 \uC0AC\uC6A9\uC790 \uC5B8\uC5B4\uB85C \uC62E\uACA8 \uC804\uB2EC\uD558\uC138\uC694. \`id\`, \`job_id\`, \`cover_letter_id\`, \`workflow_id\`, JSON \uC6D0\uBB38, \uB3C4\uAD6C \uC774\uB984\uC740 \uB0B4\uBD80 \uC791\uC5C5\uC6A9\uC73C\uB85C\uB9CC \uC4F0\uACE0 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uADF8\uB300\uB85C \uBCF5\uC0AC\uD558\uC9C0 \uB9C8\uC138\uC694.
46121
+ - \uB3C4\uAD6C \uC751\uB2F5\uC5D0 \`suggested_next_action\`\uC774 \uC788\uC73C\uBA74 \uB2E4\uC74C \uD589\uB3D9\uC744 \uAC15\uC694\uD558\uC9C0 \uB9D0\uACE0 \uC120\uD0DD\uC9C0\uB85C \uC81C\uC548\uD558\uC138\uC694.
45899
46122
  - \uBB34\uC5C7\uC744 \uD588\uACE0, \uBB34\uC5C7\uC744 \uC54C\uAC8C \uB418\uC5C8\uACE0, \uB2E4\uC74C\uC5D0 \uBB34\uC5C7\uC744 \uD560 \uC218 \uC788\uB294\uC9C0 \uC9E7\uACE0 \uBA85\uD655\uD558\uAC8C \uC804\uB2EC\uD558\uC138\uC694.
45900
46123
 
45901
46124
  7. \uB2E4\uC74C \uB2E8\uACC4\uB97C \uBA3C\uC800 \uC81C\uC548\uD569\uB2C8\uB2E4.
45902
- - \uC791\uC5C5\uC774 \uB05D\uB098\uBA74 \uC790\uC5F0\uC2A4\uB7EC\uC6B4 \uB2E4\uC74C \uD589\uB3D9\uC744 \uC81C\uC548\uD558\uC138\uC694. \uC608: \uACF5\uACE0 \uBD84\uC11D \uD6C4 \u2192 "\uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uB4DC\uB9B4\uAE4C\uC694?", \uC790\uAE30\uC18C\uAC1C\uC11C \uC800\uC7A5 \uD6C4 \u2192 "\uC9C0\uC6D0 \uC0C1\uD0DC\uB97C '\uC9C0\uC6D0 \uC644\uB8CC'\uB85C \uBC14\uAFC0\uAE4C\uC694?", \uC0C1\uD0DC\uAC00 '\uC11C\uB958 \uD569\uACA9'\uC774 \uB418\uBA74 \u2192 "\uBA74\uC811 \uC900\uBE44\uB97C \uB3C4\uC640\uB4DC\uB9B4\uAE4C\uC694?".
46125
+ - \uC791\uC5C5\uC774 \uB05D\uB098\uBA74 \uC790\uC5F0\uC2A4\uB7EC\uC6B4 \uB2E4\uC74C \uD589\uB3D9\uC744 \uC81C\uC548\uD558\uC138\uC694. \uC608: \uACF5\uACE0 \uBD84\uC11D \uD6C4 \u2192 "\uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uB4DC\uB9B4\uAE4C\uC694?", \uACBD\uB825\uAE30\uC220\uC11C \uC800\uC7A5 \uD6C4 \u2192 "\uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCDB0 \uC790\uAE30\uC18C\uAC1C\uC11C\uB3C4 \uC815\uB9AC\uD560\uAE4C\uC694?", \uC790\uAE30\uC18C\uAC1C\uC11C \uC800\uC7A5 \uD6C4 \u2192 "\uC9C0\uC6D0 \uC0C1\uD0DC\uB97C '\uC9C0\uC6D0 \uC644\uB8CC'\uB85C \uBC14\uAFC0\uAE4C\uC694?", \uC0C1\uD0DC\uAC00 '\uC11C\uB958 \uD569\uACA9'\uC774 \uB418\uBA74 \u2192 "\uBA74\uC811 \uC900\uBE44\uB97C \uB3C4\uC640\uB4DC\uB9B4\uAE4C\uC694?".
46126
+ - \uACF5\uACE0 \uBD84\uC11D \uD6C4\uC5D0\uB294 \uACE7\uC7A5 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC4F0\uC9C0 \uB9D0\uACE0, \uBC18\uB4DC\uC2DC \uC120\uD0DD\uC9C0\uB97C \uBCF4\uC5EC\uC8FC\uC138\uC694: "1. \uB124, \uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uC918" / "2. \uC544\uB2C8\uC694, \uBD84\uC11D\uB9CC \uC800\uC7A5\uD574\uC918". \uC0AC\uC6A9\uC790\uAC00 1\uBC88\uC744 \uACE0\uB978 \uACBD\uC6B0\uC5D0\uB9CC \uC790\uAE30\uC18C\uAC1C\uC11C \uC791\uC131 \uD750\uB984\uC73C\uB85C \uC774\uC5B4\uAC11\uB2C8\uB2E4.
45903
46127
  - \`get_onboarding_status\`\uC758 next_steps\uB098 \`open_dashboard\`/\`open_application\`\uC744 \uD65C\uC6A9\uD574 \uC0AC\uC6A9\uC790\uB97C \uB2E4\uC74C \uD589\uB3D9\uC73C\uB85C \uC548\uB0B4\uD558\uC138\uC694.
45904
46128
 
45905
- 8. \uAE00\uC740 AI \uD2F0 \uC548 \uB098\uAC8C \uC501\uB2C8\uB2E4.
46129
+ 8. \uC5C5\uB370\uC774\uD2B8 \uC694\uCCAD\uC740 \uBA85\uD655\uD558\uAC8C \uCC98\uB9AC\uD569\uB2C8\uB2E4.
46130
+ - \uC0AC\uC6A9\uC790\uAC00 "\uCD5C\uC2E0\uC774\uC57C?", "\uC5C5\uB370\uC774\uD2B8 \uC788\uC5B4?"\uCC98\uB7FC \uD655\uC778\uB9CC \uC694\uCCAD\uD558\uBA74 \`check_for_update\`\uB97C \uD638\uCD9C\uD558\uACE0 \uACB0\uACFC\uB97C \uC26C\uC6B4 \uB9D0\uB85C \uC54C\uB824\uC8FC\uC138\uC694.
46131
+ - \uC0AC\uC6A9\uC790\uAC00 "\uC5C5\uB370\uC774\uD2B8\uD574\uC918", "CareerMate \uC5C5\uB370\uC774\uD2B8\uD574\uC918", "\uCD5C\uC2E0\uC73C\uB85C \uC62C\uB824\uC918"\uCC98\uB7FC \uC801\uC6A9\uC744 \uBA85\uC2DC\uD558\uBA74 \`update_careermate\`\uB97C \uBC14\uB85C \uD638\uCD9C\uD558\uC138\uC694. \uC774 \uB3C4\uAD6C\uAC00 \uCD5C\uC2E0 \uBC84\uC804 \uD655\uC778\uB3C4 \uD568\uAED8 \uD569\uB2C8\uB2E4.
46132
+ - \uC5C5\uB370\uC774\uD2B8 \uC131\uACF5 \uD6C4\uC5D0\uB294 \uC801\uC6A9\uD558\uB824\uBA74 AI \uC571 \uB610\uB294 MCP \uC5F0\uACB0\uC744 \uC7AC\uC2DC\uC791\uD574\uC57C \uD55C\uB2E4\uACE0 \uC548\uB0B4\uD558\uC138\uC694. \uC0AC\uC6A9\uC790 \uCEE4\uB9AC\uC5B4 \uB370\uC774\uD130\uB294 \uB85C\uCEEC\uC5D0 \uBCF4\uC874\uB418\uACE0, npm\uC5D0\uB294 \uACF5\uAC1C \uBC84\uC804 \uC815\uBCF4 \uD655\uC778/\uC124\uCE58 \uC694\uCCAD\uB9CC \uB098\uAC11\uB2C8\uB2E4.
46133
+
46134
+ 9. \uAE00\uC740 AI \uD2F0 \uC548 \uB098\uAC8C \uC501\uB2C8\uB2E4.
45906
46135
  - \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\uB294 \`get_writing_style_guide\`\uB97C \uD638\uCD9C\uD574 \uAE00\uC4F0\uAE30 \uADDC\uCE59\uC744 \uAC00\uC838\uC624\uACE0 \uADF8\uB300\uB85C \uC801\uC6A9\uD558\uC138\uC694.
45907
- - \uBC88\uC5ED\uD22C, "\uC5F4\uC815\uC744 \uAC00\uC9C0\uACE0/\uADC0\uC0AC\uC758 \uBC1C\uC804\uC5D0 \uAE30\uC5EC" \uAC19\uC740 \uD074\uB9AC\uC170, \uAE30\uACC4\uC801 \uBCD1\uB82C(\uCCAB\uC9F8\xB7\uB458\uC9F8\xB7\uC14B\uC9F8), \uC0C1\uD22C\uC801 \uC5F0\uACB0\uC5B4, \uADE0\uC77C\uD55C \uBB38\uC7A5 \uB9AC\uB4EC\uC744 \uD53C\uD558\uACE0 \uB2F4\uBC31\uD558\uACE0 \uAD6C\uCCB4\uC801\uC73C\uB85C \uC501\uB2C8\uB2E4. \uB2E8, \uC0AC\uC2E4\xB7\uC218\uCE58\xB7\uACE0\uC720\uBA85\uC0AC\uB294 \uC808\uB300 \uBC14\uAFB8\uAC70\uB098 \uC9C0\uC5B4\uB0B4\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.
46136
+ - \uBC88\uC5ED\uD22C, "\uC5F4\uC815\uC744 \uAC00\uC9C0\uACE0/\uADC0\uC0AC\uC758 \uBC1C\uC804\uC5D0 \uAE30\uC5EC" \uAC19\uC740 \uD074\uB9AC\uC170, \uAE30\uACC4\uC801 \uBCD1\uB82C(\uCCAB\uC9F8\xB7\uB458\uC9F8\xB7\uC14B\uC9F8), \uC0C1\uD22C\uC801 \uC5F0\uACB0\uC5B4, \uADE0\uC77C\uD55C \uBB38\uC7A5 \uB9AC\uB4EC\uC744 \uD53C\uD558\uACE0 \uB2F4\uBC31\uD558\uACE0 \uAD6C\uCCB4\uC801\uC73C\uB85C \uC501\uB2C8\uB2E4. \uB2E8, \uC0AC\uC2E4\xB7\uC218\uCE58\xB7\uACE0\uC720\uBA85\uC0AC\uB294 \uC808\uB300 \uBC14\uAFB8\uAC70\uB098 \uC9C0\uC5B4\uB0B4\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC774 AI-tell \uBAA9\uB85D\uC740 \uD55C\uAD6D\uC5B4 \uAE00 \uAE30\uC900\uC774\uBA70, \uB2E4\uB978 \uC5B8\uC5B4\uB85C \uC4F8 \uB54C\uB294 \uAC19\uC740 \uC6D0\uB9AC\uB97C \uADF8 \uC5B8\uC5B4\uC758 \uC790\uC5F0\uC2A4\uB7EC\uC6B4 \uAD00\uC2B5\uC73C\uB85C \uC801\uC6A9\uD569\uB2C8\uB2E4(\uC0B0\uCD9C\uBB3C \uC5B8\uC5B4\xB7\uAD00\uD589\uC740 \uB300\uC0C1 \uACF5\uACE0\xB7\uC2DC\uC7A5\uC744 \uB530\uB984).
45908
46137
 
45909
- 9. \uC804\uBB38\uAC00 \uC808\uCC28\uB85C \uB9CC\uB4E4\uACE0, \uC800\uC7A5 \uC804 \uB2F9\uC2E0\uC774 \uC790\uAC00\uAC80\uC99D\uD569\uB2C8\uB2E4.
45910
- - \uACF5\uACE0 \uBD84\uC11D\xB7\uC790\uC18C\uC11C\xB7\uBA74\uC811 \uC790\uB8CC\uCC98\uB7FC \uC0AC\uC6A9\uC790 \uB300\uBA74 \uACB0\uACFC\uB97C \uB9CC\uB4E4\uAE30 \uC804\uC5D0\uB294, \uC989\uB2F5\uD558\uC9C0 \uB9D0\uACE0 \`get_workflow_guide\`(workflow_id \uC9C0\uC815)\uB97C \uBA3C\uC800 \uD638\uCD9C\uD574 \uADF8 \uC791\uC5C5\uC758 \uC804\uBB38\uAC00 \uC2E4\uD589 \uC808\uCC28(EOP)\uC640 \uC801\uC6A9\uD560 \uD50C\uB808\uC774\uBD81\xB7\uAC80\uC99D\uAE30 \uC21C\uC11C\uB97C \uBC1B\uC73C\uC138\uC694. \uC774\uC5B4 \`get_playbook\`\uC73C\uB85C \uB3C4\uBA54\uC778 \uC9C0\uC2DD\uC744 \uBC1B\uC544 \uC791\uC131\uD558\uACE0, save\uB85C \uC800\uC7A5\uD558\uAE30 \uC9C1\uC804 \`get_verifier\`\uB85C \uAC80\uC99D \uB8E8\uBE0C\uB9AD\uC744 \uBC1B\uC544 **\uB2F9\uC2E0\uC774 \uC9C1\uC811** \uC810\uAC80\xB7\uC218\uC815\uD55C \uB4A4 \uC800\uC7A5\uD569\uB2C8\uB2E4.
46138
+ 10. \uC804\uBB38\uAC00 \uC808\uCC28\uB85C \uB9CC\uB4E4\uACE0, \uC800\uC7A5 \uC804 \uB2F9\uC2E0\uC774 \uC790\uAC00\uAC80\uC99D\uD569\uB2C8\uB2E4.
46139
+ - \uACF5\uACE0 \uBD84\uC11D\xB7\uC790\uC18C\uC11C\xB7\uACBD\uB825\uAE30\uC220\uC11C\xB7\uBA74\uC811 \uC790\uB8CC\uCC98\uB7FC \uC0AC\uC6A9\uC790 \uB300\uBA74 \uACB0\uACFC\uB97C \uB9CC\uB4E4\uAE30 \uC804\uC5D0\uB294, \uC989\uB2F5\uD558\uC9C0 \uB9D0\uACE0 \`get_workflow_guide\`(workflow_id \uC9C0\uC815)\uB97C \uBA3C\uC800 \uD638\uCD9C\uD574 \uADF8 \uC791\uC5C5\uC758 \uC804\uBB38\uAC00 \uC2E4\uD589 \uC808\uCC28(EOP)\uC640 \uC801\uC6A9\uD560 \uD50C\uB808\uC774\uBD81\xB7\uAC80\uC99D\uAE30 \uC21C\uC11C\uB97C \uBC1B\uC73C\uC138\uC694. \uC774\uC5B4 \`get_playbook\`\uC73C\uB85C \uB3C4\uBA54\uC778 \uC9C0\uC2DD\uC744 \uBC1B\uC544 \uC791\uC131\uD558\uACE0, save\uB85C \uC800\uC7A5\uD558\uAE30 \uC9C1\uC804 \`get_verifier\`\uB85C \uAC80\uC99D \uB8E8\uBE0C\uB9AD\uC744 \uBC1B\uC544 **\uB2F9\uC2E0\uC774 \uC9C1\uC811** \uC810\uAC80\xB7\uC218\uC815\uD55C \uB4A4 \uC800\uC7A5\uD569\uB2C8\uB2E4.
45911
46140
  - CareerMate\uB294 \uC2AC\uB86D \uBC00\uB3C4\xB7\uD0A4\uC6CC\uB4DC \uB9E4\uCE6D\uCC98\uB7FC \uC140 \uC218 \uC788\uB294 \uD56D\uBAA9\uB9CC \uBCF4\uC870\uB85C \uB3D5\uACE0, \uC758\uBBF8 \uD310\uB2E8(\uC774 \uAE00\uC774 \uC9C8\uBB38\uC5D0 \uB2F5\uD558\uB294\uAC00\xB7\uD1A4\uC774 \uB9DE\uB294\uAC00)\uC740 \uB2F9\uC2E0\uC774 \uD569\uB2C8\uB2E4. \uC0AC\uC2E4\xB7\uC218\uCE58\xB7\uACE0\uC720\uBA85\uC0AC\uB294 \uADF8\uB300\uB85C \uBCF4\uC874\uD569\uB2C8\uB2E4.
45912
46141
 
45913
46142
  # \uC791\uC5C5 \uBC29\uC2DD \uC694\uC57D
@@ -45925,7 +46154,7 @@ var INSTALL_PROMPT_CLAUDE = `Claude\uC57C, \uC544\uB798 CareerMate \uC124\uCE58
45925
46154
  4. \uADF8\uB2E4\uC74C \uB098\uC5D0\uAC8C \uC774\uB825\uC11C\uB098 \uC790\uAE30\uC18C\uAC1C\uC11C \uD30C\uC77C\uC744 \uC5C5\uB85C\uB4DC\uD558\uB77C\uACE0 \uC548\uB0B4\uD574\uC918.
45926
46155
  5. \uB0B4\uAC00 \uC62C\uB9B0 \uD30C\uC77C\uC744 \uBC14\uD0D5\uC73C\uB85C \uB0B4 \uD504\uB85C\uD544\uC744 \uAD6C\uC870\uD654\uD574\uC11C \`save_profile\`\uB85C CareerMate\uC5D0 \uC800\uC7A5\uD558\uACE0, \uC628\uBCF4\uB529\uC744 \uC2DC\uC791\uD574\uC918.
45927
46156
 
45928
- \uAE30\uC220 \uC6A9\uC5B4\uB294 \uCD5C\uB300\uD55C \uBE7C\uACE0, \uB9E4 \uB2E8\uACC4\uB9C8\uB2E4 \uC9C0\uAE08 \uBB58 \uD558\uACE0 \uC788\uB294\uC9C0 \uC26C\uC6B4 \uD55C\uAD6D\uC5B4\uB85C \uC54C\uB824\uC918. \uB9C9\uD788\uB294 \uBD80\uBD84\uC774 \uC788\uC73C\uBA74 \uCD94\uCE21\uD558\uC9C0 \uB9D0\uACE0 \uB098\uC5D0\uAC8C \uBB3C\uC5B4\uBD10\uC918.`;
46157
+ \uAE30\uC220 \uC6A9\uC5B4\uB294 \uCD5C\uB300\uD55C \uBE7C\uACE0, \uB9E4 \uB2E8\uACC4\uB9C8\uB2E4 \uC9C0\uAE08 \uBB58 \uD558\uACE0 \uC788\uB294\uC9C0 \uB0B4\uAC00 \uC4F0\uB294 \uC5B8\uC5B4\uB85C \uC27D\uAC8C \uC54C\uB824\uC918. \uB9C9\uD788\uB294 \uBD80\uBD84\uC774 \uC788\uC73C\uBA74 \uCD94\uCE21\uD558\uC9C0 \uB9D0\uACE0 \uB098\uC5D0\uAC8C \uBB3C\uC5B4\uBD10\uC918.`;
45929
46158
  var INSTALL_PROMPT_CHATGPT = `ChatGPT\uC57C, \uC544\uB798 \uD398\uC774\uC9C0\uB97C \uC77D\uACE0 \uB0B4 \uCEF4\uD4E8\uD130\uC5D0 CareerMate\uB97C \uC124\uCE58\uD558\uACE0 \uC124\uC815\uD574\uC918. \uC124\uCE58\uAC00 \uB05D\uB098\uBA74 \uC5F0\uACB0 \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uACE0, \uB098\uC5D0\uAC8C \uC774\uB825\uC11C\uB098 \uC790\uAE30\uC18C\uAC1C\uC11C \uD30C\uC77C\uC744 \uC5C5\uB85C\uB4DC\uD558\uB77C\uACE0 \uC548\uB0B4\uD574\uC918. \uADF8\uB2E4\uC74C \uB0B4 \uD504\uB85C\uD544\uC744 \uAD6C\uC870\uD654\uD574\uC11C CareerMate\uC5D0 \uC800\uC7A5\uD574\uC918.
45930
46159
 
45931
46160
  \uAD6C\uCCB4\uC801\uC73C\uB85C\uB294 \uC774\uB807\uAC8C \uD574\uC918.
@@ -45935,7 +46164,7 @@ var INSTALL_PROMPT_CHATGPT = `ChatGPT\uC57C, \uC544\uB798 \uD398\uC774\uC9C0\uB9
45935
46164
  4. \uC774\uB825\uC11C\xB7\uC790\uAE30\uC18C\uAC1C\uC11C \uD30C\uC77C\uC744 \uC5C5\uB85C\uB4DC\uD558\uB77C\uACE0 \uB098\uC5D0\uAC8C \uC548\uB0B4\uD574\uC918.
45936
46165
  5. \uC62C\uB9B0 \uD30C\uC77C\uB85C \uB0B4 \uD504\uB85C\uD544\uC744 \uC815\uB9AC\uD574\uC11C \`save_profile\`\uB85C \uC800\uC7A5\uD558\uACE0 \uC628\uBCF4\uB529\uC744 \uC2DC\uC791\uD574\uC918.
45937
46166
 
45938
- \uC9C4\uD589 \uC0C1\uD669\uC740 \uD56D\uC0C1 \uC26C\uC6B4 \uD55C\uAD6D\uC5B4\uB85C \uC124\uBA85\uD558\uACE0, \uD655\uC2E4\uD558\uC9C0 \uC54A\uC740 \uC815\uBCF4\uB294 \uC9C0\uC5B4\uB0B4\uC9C0 \uB9D0\uACE0 \uB098\uC5D0\uAC8C \uBB3C\uC5B4\uBD10\uC918.`;
46167
+ \uC9C4\uD589 \uC0C1\uD669\uC740 \uD56D\uC0C1 \uB0B4\uAC00 \uC4F0\uB294 \uC5B8\uC5B4\uB85C \uC27D\uAC8C \uC124\uBA85\uD558\uACE0, \uD655\uC2E4\uD558\uC9C0 \uC54A\uC740 \uC815\uBCF4\uB294 \uC9C0\uC5B4\uB0B4\uC9C0 \uB9D0\uACE0 \uB098\uC5D0\uAC8C \uBB3C\uC5B4\uBD10\uC918.`;
45939
46168
  var INSTALL_PROMPT_GENERIC = `\uC548\uB155, \uC544\uB798 CareerMate \uC124\uCE58 \uC548\uB0B4(\uC124\uCE58 \uD398\uC774\uC9C0 \uB610\uB294 INSTALL.md)\uB97C \uC77D\uACE0 \uB0B4 \uCEF4\uD4E8\uD130\uC5D0 CareerMate\uB97C \uC124\uCE58\uD558\uACE0 \uC124\uC815\uD574\uC918.
45940
46169
 
45941
46170
  CareerMate\uB294 \uB0B4 \uCEE4\uB9AC\uC5B4 \uB370\uC774\uD130(\uD504\uB85C\uD544\xB7\uC774\uB825\uC11C\xB7\uC790\uAE30\uC18C\uAC1C\uC11C\xB7\uC9C0\uC6D0 \uD604\uD669)\uB97C \uB0B4 \uCEF4\uD4E8\uD130\uC5D0 \uC800\uC7A5\uD558\uACE0, MCP\uB97C \uD1B5\uD574 \uB108(\uB0B4 AI)\uAC00 \uADF8 \uB370\uC774\uD130\uB97C \uC77D\uACE0 \uC4F8 \uC218 \uC788\uAC8C \uD574\uC8FC\uB294 \uB3C4\uAD6C\uC57C. \uBD84\uC11D\uACFC \uC791\uC131\uC740 \uC804\uBD80 \uB124\uAC00 \uD574.
@@ -45943,21 +46172,21 @@ CareerMate\uB294 \uB0B4 \uCEE4\uB9AC\uC5B4 \uB370\uC774\uD130(\uD504\uB85C\uD544
45943
46172
  \uC774\uB807\uAC8C \uC9C4\uD589\uD574\uC918.
45944
46173
  1. \uC124\uCE58 \uC548\uB0B4 \uBB38\uC11C\uB97C \uB05D\uAE4C\uC9C0 \uC77D\uACE0 \uADF8\uB300\uB85C \uB530\uB77C \uC124\uCE58\uD574.
45945
46174
  2. \uB124\uAC00 \uC4F0\uB294 AI \uB3C4\uAD6C\uC5D0 CareerMate MCP \uC11C\uBC84\uB97C \uC5F0\uACB0(\uC124\uC815)\uD574\uC918.
45946
- 3. \uC5F0\uACB0 \uD6C4 \`get_onboarding_status\`\uB97C \uD638\uCD9C\uD574 \uC5F0\uACB0 \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uACE0, \uACB0\uACFC\uB97C \uC26C\uC6B4 \uD55C\uAD6D\uC5B4\uB85C \uC54C\uB824\uC918.
46175
+ 3. \uC5F0\uACB0 \uD6C4 \`get_onboarding_status\`\uB97C \uD638\uCD9C\uD574 \uC5F0\uACB0 \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uACE0, \uACB0\uACFC\uB97C \uB0B4\uAC00 \uC4F0\uB294 \uC5B8\uC5B4\uB85C \uC27D\uAC8C \uC54C\uB824\uC918.
45947
46176
  4. \uB098\uC5D0\uAC8C \uC774\uB825\uC11C\uB098 \uC790\uAE30\uC18C\uAC1C\uC11C \uD30C\uC77C\uC744 \uC5C5\uB85C\uB4DC\uD558\uB77C\uACE0 \uC548\uB0B4\uD574\uC918.
45948
46177
  5. \uC62C\uB9B0 \uD30C\uC77C\uB85C \uB0B4 \uD504\uB85C\uD544\uC744 \uAD6C\uC870\uD654\uD574\uC11C \`save_profile\`\uB85C \uC800\uC7A5\uD558\uACE0 \uC628\uBCF4\uB529\uC744 \uC2DC\uC791\uD574\uC918.
45949
46178
 
45950
- \uB9E4 \uB2E8\uACC4\uB9C8\uB2E4 \uC26C\uC6B4 \uD55C\uAD6D\uC5B4\uB85C \uC124\uBA85\uD558\uACE0, \uBD88\uD655\uC2E4\uD55C \uB0B4\uC6A9\uC740 \uC9C0\uC5B4\uB0B4\uC9C0 \uB9D0\uACE0 \uB098\uC5D0\uAC8C \uD655\uC778\uD574\uC918.`;
46179
+ \uB9E4 \uB2E8\uACC4\uB9C8\uB2E4 \uB0B4\uAC00 \uC4F0\uB294 \uC5B8\uC5B4\uB85C \uC27D\uAC8C \uC124\uBA85\uD558\uACE0, \uBD88\uD655\uC2E4\uD55C \uB0B4\uC6A9\uC740 \uC9C0\uC5B4\uB0B4\uC9C0 \uB9D0\uACE0 \uB098\uC5D0\uAC8C \uD655\uC778\uD574\uC918.`;
45951
46180
 
45952
46181
  // packages/prompts/src/onboarding.ts
45953
- var ONBOARDING_PROMPT = `\uC774\uC81C CareerMate\uC5D0 \uC5F0\uACB0\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uC758 \uCEE4\uB9AC\uC5B4 \uB370\uC774\uD130 \uCCAB \uC14B\uC5C5(\uC628\uBCF4\uB529)\uC744 \uB3C4\uC640\uC8FC\uC138\uC694.
46182
+ var ONBOARDING_PROMPT = `\uC774\uC81C CareerMate\uC5D0 \uC5F0\uACB0\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uC758 \uCEE4\uB9AC\uC5B4 \uB370\uC774\uD130 \uCCAB \uC14B\uC5C5(\uC628\uBCF4\uB529)\uC744 \uB3C4\uC640\uC8FC\uC138\uC694. \uC0AC\uC6A9\uC790\uAC00 \uC4F0\uB294 \uC5B8\uC5B4\uB85C \uC548\uB0B4\uD558\uC138\uC694 \u2014 \uC774 \uC548\uB0B4\uBB38\uC774 \uD55C\uAD6D\uC5B4\uB77C\uB3C4 \uC0AC\uC6A9\uC790\uAC00 \uB2E4\uB978 \uC5B8\uC5B4\uB85C \uB9D0\uD558\uBA74 \uADF8 \uC5B8\uC5B4\uB85C \uB2F5\uD569\uB2C8\uB2E4.
45954
46183
 
45955
46184
  # 1\uB2E8\uACC4 \u2014 \uD604\uC7AC \uC0C1\uD0DC \uD655\uC778
45956
46185
  - \uBA3C\uC800 \`get_onboarding_status\`\uB97C \uD638\uCD9C\uD558\uC138\uC694. \uACB0\uACFC\uC758 has_profile / has_resume / has_cover_letter / has_experience / has_skills / has_job, profile_completeness(\uC644\uC131\uB3C4), next_steps(\uB2E4\uC74C \uB2E8\uACC4)\uB97C \uD655\uC778\uD569\uB2C8\uB2E4.
45957
- - \uC0AC\uC6A9\uC790\uC5D0\uAC8C "\uC9C0\uAE08\uAE4C\uC9C0 \uB4F1\uB85D\uB41C \uC815\uBCF4"\uC640 "\uC55E\uC73C\uB85C \uCC44\uC6B0\uBA74 \uC88B\uC740 \uC815\uBCF4"\uB97C \uC26C\uC6B4 \uD55C\uAD6D\uC5B4\uB85C \uC9E7\uAC8C \uC548\uB0B4\uD558\uC138\uC694. (\uC774\uBBF8 \uCC44\uC6CC\uC9C4 \uD56D\uBAA9\uC740 \uB2E4\uC2DC \uBB3B\uC9C0 \uB9C8\uC138\uC694.)
46186
+ - \uC0AC\uC6A9\uC790\uC5D0\uAC8C "\uC9C0\uAE08\uAE4C\uC9C0 \uB4F1\uB85D\uB41C \uC815\uBCF4"\uC640 "\uC55E\uC73C\uB85C \uCC44\uC6B0\uBA74 \uC88B\uC740 \uC815\uBCF4"\uB97C \uC0AC\uC6A9\uC790\uC758 \uC5B8\uC5B4\uB85C \uC27D\uACE0 \uC9E7\uAC8C \uC548\uB0B4\uD558\uC138\uC694. (\uC774\uBBF8 \uCC44\uC6CC\uC9C4 \uD56D\uBAA9\uC740 \uB2E4\uC2DC \uBB3B\uC9C0 \uB9C8\uC138\uC694.)
45958
46187
 
45959
46188
  # 2\uB2E8\uACC4 \u2014 \uD504\uB85C\uD544 \uC218\uC9D1 \uBC0F \uC800\uC7A5
45960
- - \uC0AC\uC6A9\uC790\uAC00 \uC62C\uB9B0 \uC774\uB825\uC11C/\uC790\uAE30\uC18C\uAC1C\uC11C \uD30C\uC77C\uC774\uB098 \uC0AC\uC6A9\uC790\uAC00 \uC9C1\uC811 \uC54C\uB824\uC900 \uB0B4\uC6A9\uC744 \uBC14\uD0D5\uC73C\uB85C \uD504\uB85C\uD544\uC744 \uAD6C\uC870\uD654\uD558\uC138\uC694.
46189
+ - \uC0AC\uC6A9\uC790\uAC00 \uC62C\uB9B0 \uC774\uB825\uC11C/\uACBD\uB825\uAE30\uC220\uC11C/\uD3EC\uD2B8\uD3F4\uB9AC\uC624/\uC790\uAE30\uC18C\uAC1C\uC11C \uD30C\uC77C\uC774\uB098 \uC0AC\uC6A9\uC790\uAC00 \uC9C1\uC811 \uC54C\uB824\uC900 \uB0B4\uC6A9\uC744 \uBC14\uD0D5\uC73C\uB85C \uD504\uB85C\uD544\uC744 \uAD6C\uC870\uD654\uD558\uC138\uC694.
45961
46190
  - \uC774\uB984, \uC5F0\uB77D\uCC98, \uD55C \uC904 \uC18C\uAC1C(headline), \uC790\uAE30\uC18C\uAC1C \uC694\uC57D(summary), \uD76C\uB9DD \uC9C1\uBB34(desired_roles), \uD76C\uB9DD \uADFC\uBB34 \uC870\uAC74(desired_conditions)\uC744 \uC815\uB9AC\uD569\uB2C8\uB2E4.
45962
46191
  - \uAE00\uC4F0\uAE30 \uC120\uD638\uB3C4 \uD568\uAED8 \uBB3C\uC5B4\uBCF4\uC138\uC694: \uC120\uD638 \uBB38\uCCB4(preferred_tone, \uC608: "\uB2F4\uBC31\uD558\uACE0 \uAD6C\uCCB4\uC801")\uC640 \uAC15\uC870\uD558\uACE0 \uC2F6\uC740 \uD575\uC2EC \uD3EC\uC778\uD2B8(emphasis_points). \uC774 \uC815\uBCF4\uB294 \uC774\uD6C4 \uC790\uAE30\uC18C\uAC1C\uC11C \uC791\uC131\uC5D0 \uADF8\uB300\uB85C \uD65C\uC6A9\uB429\uB2C8\uB2E4.
45963
46192
  - \uC815\uB9AC\uD55C \uB0B4\uC6A9\uC744 \`save_profile\`\uB85C \uC800\uC7A5\uD558\uC138\uC694. \uD30C\uC77C\uC5D0\uC11C \uC77D\uC5B4\uC628 \uC815\uBCF4\uB294 source\uB97C 'upload', \uC0AC\uC6A9\uC790\uAC00 \uB9D0\uB85C \uC54C\uB824\uC900 \uC815\uBCF4\uB294 'manual'\uB85C \uD45C\uC2DC\uD558\uC138\uC694.
@@ -45976,7 +46205,7 @@ var ONBOARDING_PROMPT = `\uC774\uC81C CareerMate\uC5D0 \uC5F0\uACB0\uB418\uC5C8\
45976
46205
  - \uB2E4\uC2DC \`get_onboarding_status\`\uB85C \uC644\uC131\uB3C4\uB97C \uD655\uC778\uD558\uACE0, \uBE44\uC5B4 \uC788\uB294 \uBD80\uBD84\uC774 \uC788\uC73C\uBA74 \uCC44\uC6B0\uC790\uACE0 \uC81C\uC548\uD558\uC138\uC694.
45977
46206
  - \uB9C8\uBB34\uB9AC\uB85C \uB2E4\uC74C \uD589\uB3D9\uC744 \uC81C\uC548\uD558\uC138\uC694. \uC608: "\uC9C0\uC6D0\uD558\uACE0 \uC2F6\uC740 \uACF5\uACE0\uAC00 \uC788\uC73C\uBA74 \uB9C1\uD06C\uB098 \uB0B4\uC6A9\uC744 \uBD99\uC5EC\uC8FC\uC138\uC694. \uC801\uD569\uB3C4\uB97C \uBD84\uC11D\uD574\uB4DC\uB9B4\uAC8C\uC694."
45978
46207
 
45979
- \uC9C4\uD589 \uB0B4\uB0B4 \uAE30\uC220 \uC6A9\uC5B4(MCP, job_id \uB4F1)\uB294 \uBE7C\uACE0, \uC9C0\uAE08 \uBB34\uC5C7\uC744 \uD558\uACE0 \uC788\uB294\uC9C0 \uC26C\uC6B4 \uD55C\uAD6D\uC5B4\uB85C \uC54C\uB824\uC8FC\uC138\uC694.`;
46208
+ \uC9C4\uD589 \uB0B4\uB0B4 \uAE30\uC220 \uC6A9\uC5B4(MCP, job_id \uB4F1)\uB294 \uBE7C\uACE0, \uC9C0\uAE08 \uBB34\uC5C7\uC744 \uD558\uACE0 \uC788\uB294\uC9C0 \uC0AC\uC6A9\uC790\uC758 \uC5B8\uC5B4\uB85C \uC27D\uAC8C \uC54C\uB824\uC8FC\uC138\uC694.`;
45980
46209
 
45981
46210
  // packages/prompts/src/humanize.ts
45982
46211
  var HUMANIZE_WRITING_GUIDE = `# \uC0AC\uB78C\uC774 \uC4F4 \uAC83\uCC98\uB7FC: \uC790\uAE30\uC18C\uAC1C\uC11C\xB7\uCEE4\uB9AC\uC5B4 \uAE00\uC4F0\uAE30 \uADDC\uCE59
@@ -45984,6 +46213,8 @@ var HUMANIZE_WRITING_GUIDE = `# \uC0AC\uB78C\uC774 \uC4F4 \uAC83\uCC98\uB7FC: \u
45984
46213
  \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.
45985
46214
  \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.
45986
46215
 
46216
+ > **\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.
46217
+
45987
46218
  ## \uC808\uB300 \uC6D0\uCE59
45988
46219
  - \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.
45989
46220
  - \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.
@@ -46075,7 +46306,7 @@ var WORKFLOWS = [
46075
46306
  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.',
46076
46307
  steps: [
46077
46308
  "`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.",
46078
- '\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).',
46309
+ '\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).',
46079
46310
  '\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.)',
46080
46311
  "\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.",
46081
46312
  "\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).",
@@ -46096,9 +46327,9 @@ var WORKFLOWS = [
46096
46327
  "\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.",
46097
46328
  "\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.",
46098
46329
  "\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).",
46099
- "\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.",
46100
- '"\uC774 \uACF5\uACE0\uC5D0 \uB9DE\uCD98 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC368\uB4DC\uB9B4\uAE4C\uC694?"\uB77C\uACE0 \uC790\uAE30\uC18C\uAC1C\uC11C \uC791\uC131\uC744 \uC81C\uC548\uD55C\uB2E4.',
46101
- '\uC0AC\uC6A9\uC790\uAC00 \uB3D9\uC758\uD558\uBA74 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uC791\uC131\uD558\uAE30 \uC9C1\uC804\uC5D0 `get_writing_style_guide`\uB97C \uD638\uCD9C\uD574 "AI \uD2F0 \uC548 \uB098\uB294 \uAE00\uC4F0\uAE30 \uADDC\uCE59"\uC744 \uAC00\uC838\uC628\uB2E4.',
46330
+ "\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.",
46331
+ '\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".',
46332
+ '\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.',
46102
46333
  "\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).",
46103
46334
  "`open_application`\uC73C\uB85C \uD574\uB2F9 \uC9C0\uC6D0 \uAC74\uC744 \uC5F4\uC5B4 \uC0AC\uC6A9\uC790\uAC00 \uACB0\uACFC\uB97C \uD655\uC778\uD558\uAC8C \uD558\uACE0, \uC9C0\uC6D0 \uC0C1\uD0DC \uBCC0\uACBD(\uC608: \uC9C0\uC6D0 \uC608\uC815/\uC9C0\uC6D0 \uC644\uB8CC)\uC744 \uC81C\uC548\uD55C\uB2E4."
46104
46335
  ]
@@ -46115,7 +46346,24 @@ var WORKFLOWS = [
46115
46346
  "\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.",
46116
46347
  "\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).",
46117
46348
  "\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.",
46118
- "\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."
46349
+ "\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."
46350
+ ]
46351
+ },
46352
+ {
46353
+ id: "write_career_description",
46354
+ title: "\uACBD\uB825\uAE30\uC220\uC11C \uC791\uC131",
46355
+ 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.",
46356
+ 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.',
46357
+ steps: [
46358
+ "`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.",
46359
+ "\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.",
46360
+ '`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.',
46361
+ "\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.",
46362
+ "\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.",
46363
+ '\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.',
46364
+ '\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.',
46365
+ "\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.",
46366
+ "\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."
46119
46367
  ]
46120
46368
  },
46121
46369
  {
@@ -46126,7 +46374,7 @@ var WORKFLOWS = [
46126
46374
  steps: [
46127
46375
  "\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.",
46128
46376
  "\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.",
46129
- "\uBCC0\uACBD \uACB0\uACFC\uB97C \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC26C\uC6B4 \uD55C\uAD6D\uC5B4\uB85C \uC54C\uB9B0\uB2E4.",
46377
+ "\uBCC0\uACBD \uACB0\uACFC\uB97C \uC0AC\uC6A9\uC790\uC758 \uC5B8\uC5B4\uB85C \uC27D\uAC8C \uC54C\uB9B0\uB2E4.",
46130
46378
  "\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.",
46131
46379
  "\uD544\uC694\uD558\uBA74 `open_application`\uC73C\uB85C \uD574\uB2F9 \uC9C0\uC6D0 \uAC74\uC744 \uC5F4\uC5B4 \uBCF4\uC5EC\uC900\uB2E4."
46132
46380
  ]
@@ -46134,14 +46382,29 @@ var WORKFLOWS = [
46134
46382
  {
46135
46383
  id: "prepare_interview",
46136
46384
  title: "\uBA74\uC811 \uC900\uBE44",
46137
- 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.",
46138
- 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.',
46385
+ 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.",
46386
+ 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.',
46139
46387
  steps: [
46140
46388
  "`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.",
46141
46389
  "\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.",
46142
46390
  "\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.",
46143
46391
  "\uC900\uBE44 \uB0B4\uC6A9\uC744 `save_interview_prep`\uB85C \uC800\uC7A5\uD55C\uB2E4(job_id, questions, star_guides, self_introduction, notes).",
46144
- "\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."
46392
+ "\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."
46393
+ ]
46394
+ },
46395
+ {
46396
+ id: "build_personal_brand",
46397
+ title: "\uD37C\uC2A4\uB110 \uBE0C\uB79C\uB4DC (LinkedIn\xB7\uD3EC\uD2B8\uD3F4\uB9AC\uC624)",
46398
+ 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.",
46399
+ 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.',
46400
+ steps: [
46401
+ "`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.",
46402
+ '`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.',
46403
+ "\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.",
46404
+ "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.",
46405
+ '\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.',
46406
+ "\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.",
46407
+ "\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."
46145
46408
  ]
46146
46409
  }
46147
46410
  ];
@@ -46683,6 +46946,14 @@ function resumeToMarkdown(doc, profile) {
46683
46946
  content: body
46684
46947
  };
46685
46948
  }
46949
+ function resumeToHtml(doc, profile) {
46950
+ const md = resumeToMarkdown(doc, profile);
46951
+ return {
46952
+ filename: `${slugify(doc.title, doc.kind)}.html`,
46953
+ mimeType: HTML_MIME,
46954
+ content: toPrintableHtml(doc.title, md.content)
46955
+ };
46956
+ }
46686
46957
  function profileToMarkdown(profile, experiences = [], projects = [], skills = []) {
46687
46958
  const name = profile.name?.trim() || "\uC774\uB825\uC11C";
46688
46959
  const contact = [];
@@ -46764,6 +47035,15 @@ function profileToMarkdown(profile, experiences = [], projects = [], skills = []
46764
47035
  content
46765
47036
  };
46766
47037
  }
47038
+ function profileToHtml(profile, experiences = [], projects = [], skills = []) {
47039
+ const md = profileToMarkdown(profile, experiences, projects, skills);
47040
+ const title = profile.name?.trim() || "\uC774\uB825\uC11C";
47041
+ return {
47042
+ filename: `${slugify(title, "resume")}-resume.html`,
47043
+ mimeType: HTML_MIME,
47044
+ content: toPrintableHtml(title, md.content)
47045
+ };
47046
+ }
46767
47047
  function interviewPrepToMarkdown(prep, job) {
46768
47048
  const titleBase = job ? `${job.company} ${job.position} \uBA74\uC811 \uC900\uBE44` : "\uBA74\uC811 \uC900\uBE44";
46769
47049
  const meta = [];
@@ -46772,17 +47052,22 @@ function interviewPrepToMarkdown(prep, job) {
46772
47052
  if (job.deadline) meta.push(kv("\uB9C8\uAC10", job.deadline));
46773
47053
  }
46774
47054
  const introBlock = prep.self_introduction?.trim() ? joinBlocks(["## 1\uBD84 \uC790\uAE30\uC18C\uAC1C", prep.self_introduction.trim()]) : "";
46775
- const questionsBlock = prep.questions?.length ? joinBlocks([
46776
- "## \uC608\uC0C1 \uC9C8\uBB38",
46777
- ...prep.questions.map(
46778
- (q, idx) => joinBlocks([
46779
- `### Q${idx + 1}. ${q.question}`,
46780
- q.intent?.trim() ? kv("\uC758\uB3C4", q.intent) : "",
46781
- q.answer_outline?.trim() ? joinBlocks(["**\uB2F5\uBCC0 \uAC00\uC774\uB4DC**", q.answer_outline.trim()]) : "",
46782
- q.followups?.length ? joinBlocks(["**\uC608\uC0C1 \uAF2C\uB9AC \uC9C8\uBB38**", bulletList(q.followups)]) : ""
46783
- ])
46784
- )
46785
- ]) : "";
47055
+ const renderQuestion = (q, idx, level) => joinBlocks([
47056
+ `${level} Q${idx + 1}. ${q.question}`,
47057
+ q.intent?.trim() ? kv("\uC758\uB3C4", q.intent) : "",
47058
+ q.answer_outline?.trim() ? joinBlocks(["**\uB2F5\uBCC0 \uAC00\uC774\uB4DC**", q.answer_outline.trim()]) : "",
47059
+ q.followups?.length ? joinBlocks(["**\uC608\uC0C1 \uAF2C\uB9AC \uC9C8\uBB38**", bulletList(q.followups)]) : ""
47060
+ ]);
47061
+ let questionsBlock = "";
47062
+ if (prep.questions?.length) {
47063
+ const technical = prep.questions.filter((q) => (q.category ?? "technical") === "technical");
47064
+ const behavioral = prep.questions.filter((q) => q.category === "behavioral");
47065
+ questionsBlock = behavioral.length ? joinBlocks([
47066
+ "## \uC608\uC0C1 \uC9C8\uBB38",
47067
+ technical.length ? joinBlocks(["### \uAE30\uC220 \uBA74\uC811", ...technical.map((q, i) => renderQuestion(q, i, "####"))]) : "",
47068
+ joinBlocks(["### \uC778\uC131\xB7\uCEEC\uCC98\uD54F \uBA74\uC811", ...behavioral.map((q, i) => renderQuestion(q, i, "####"))])
47069
+ ]) : joinBlocks(["## \uC608\uC0C1 \uC9C8\uBB38", ...prep.questions.map((q, i) => renderQuestion(q, i, "###"))]);
47070
+ }
46786
47071
  const starBlock = prep.star_guides?.length ? joinBlocks([
46787
47072
  "## STAR \uC815\uB9AC",
46788
47073
  ...prep.star_guides.map(
@@ -46811,6 +47096,15 @@ function interviewPrepToMarkdown(prep, job) {
46811
47096
  content
46812
47097
  };
46813
47098
  }
47099
+ function interviewPrepToHtml(prep, job) {
47100
+ const md = interviewPrepToMarkdown(prep, job);
47101
+ const title = job ? `${job.company} ${job.position} \uBA74\uC811 \uC900\uBE44` : "\uBA74\uC811 \uC900\uBE44";
47102
+ return {
47103
+ filename: `${slugify(title, "interview-prep")}.html`,
47104
+ mimeType: HTML_MIME,
47105
+ content: toPrintableHtml(title, md.content)
47106
+ };
47107
+ }
46814
47108
 
46815
47109
  // apps/web/src/exports.ts
46816
47110
  function asFormat(result2, format) {
@@ -46834,20 +47128,25 @@ function exportCoverLetter(id3, format) {
46834
47128
  function exportDocument(id3, format) {
46835
47129
  const doc = documentRepo.get(id3);
46836
47130
  if (!doc) throw new HttpError(404, "\uBB38\uC11C\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
46837
- return asFormat(resumeToMarkdown(doc, profileRepo.get()), format);
47131
+ const profile = profileRepo.get();
47132
+ if (format === "html") return resumeToHtml(doc, profile);
47133
+ return asFormat(resumeToMarkdown(doc, profile), format);
46838
47134
  }
46839
47135
  function exportProfile(format) {
46840
47136
  const profile = profileRepo.get();
46841
47137
  if (!profile) throw new HttpError(404, "\uD504\uB85C\uD544\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
46842
- return asFormat(
46843
- profileToMarkdown(profile, experienceRepo.list(), projectRepo.list(), skillRepo.list()),
46844
- format
46845
- );
47138
+ const experiences = experienceRepo.list();
47139
+ const projects = projectRepo.list();
47140
+ const skills = skillRepo.list();
47141
+ if (format === "html") return profileToHtml(profile, experiences, projects, skills);
47142
+ return asFormat(profileToMarkdown(profile, experiences, projects, skills), format);
46846
47143
  }
46847
47144
  function exportInterview(jobId, format) {
46848
47145
  const prep = interviewRepo.getByJob(jobId);
46849
47146
  if (!prep) throw new HttpError(404, "\uBA74\uC811 \uC900\uBE44 \uC790\uB8CC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
46850
- return asFormat(interviewPrepToMarkdown(prep, jobRepo.get(jobId)), format);
47147
+ const job = jobRepo.get(jobId);
47148
+ if (format === "html") return interviewPrepToHtml(prep, job);
47149
+ return asFormat(interviewPrepToMarkdown(prep, job), format);
46851
47150
  }
46852
47151
 
46853
47152
  // apps/web/src/info.ts
@@ -46874,20 +47173,287 @@ function getServerInfo() {
46874
47173
 
46875
47174
  // apps/web/src/settings.ts
46876
47175
  import fs4 from "node:fs";
47176
+ import os2 from "node:os";
46877
47177
  import path4 from "node:path";
47178
+ import { spawn } from "node:child_process";
47179
+ import { fileURLToPath } from "node:url";
47180
+ var __dirname = path4.dirname(fileURLToPath(import.meta.url));
46878
47181
  function listTables(db) {
46879
47182
  return db.prepare(
46880
47183
  `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name != '_meta' ORDER BY name`
46881
47184
  ).all().map((r) => r.name);
46882
47185
  }
47186
+ var RESTORE_ORDER = [
47187
+ "profile",
47188
+ "experiences",
47189
+ "projects",
47190
+ "skills",
47191
+ "documents",
47192
+ "jobs",
47193
+ "cover_letters",
47194
+ "cover_letter_versions",
47195
+ "fit_analyses",
47196
+ "applications",
47197
+ "interview_preps",
47198
+ "application_timeline_events",
47199
+ "activities"
47200
+ ];
47201
+ function quoteIdent(name) {
47202
+ return `"${name.replace(/"/g, '""')}"`;
47203
+ }
47204
+ function shQuote(value) {
47205
+ return `'${value.replace(/'/g, `'\\''`)}'`;
47206
+ }
47207
+ function cmdQuote(value) {
47208
+ return value.replace(/"/g, '""');
47209
+ }
47210
+ function schemaVersion(db) {
47211
+ const ver = db.prepare(`SELECT value FROM _meta WHERE key='schema_version'`).get();
47212
+ return Number(ver?.value ?? 0);
47213
+ }
47214
+ function tableColumns(db, table) {
47215
+ return db.prepare(`PRAGMA table_info(${quoteIdent(table)})`).all().map((r) => r.name);
47216
+ }
47217
+ function isRecord(v) {
47218
+ return typeof v === "object" && v !== null && !Array.isArray(v);
47219
+ }
47220
+ function appRoot() {
47221
+ return BUNDLED ? __dirname : path4.resolve(__dirname, "..", "..", "..");
47222
+ }
47223
+ function shortcutFolder() {
47224
+ const override = process.env.CAREERMATE_SHORTCUT_DIR?.trim();
47225
+ if (override) return override;
47226
+ const desktop = path4.join(os2.homedir(), "Desktop");
47227
+ const parent = fs4.existsSync(desktop) ? desktop : getExportsDir();
47228
+ return path4.join(parent, "CareerMate \uB300\uC2DC\uBCF4\uB4DC");
47229
+ }
47230
+ function openFolder(dir) {
47231
+ try {
47232
+ if (process.platform === "win32") spawn("explorer", [dir], { stdio: "ignore", detached: true }).unref();
47233
+ else if (process.platform === "darwin") spawn("open", [dir], { stdio: "ignore", detached: true }).unref();
47234
+ else spawn("xdg-open", [dir], { stdio: "ignore", detached: true }).unref();
47235
+ } catch {
47236
+ }
47237
+ }
47238
+ function parseBackup(raw) {
47239
+ if (!isRecord(raw) || !isRecord(raw.tables)) {
47240
+ throw new Error("CareerMate \uBC31\uC5C5 JSON \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4.");
47241
+ }
47242
+ const rawTables = raw.tables;
47243
+ const db = getDb();
47244
+ const currentVersion = schemaVersion(db);
47245
+ const version = Number(raw.version);
47246
+ if (!Number.isInteger(version) || version < 0) {
47247
+ throw new Error("\uBC31\uC5C5 \uD30C\uC77C\uC758 \uC2A4\uD0A4\uB9C8 \uBC84\uC804\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
47248
+ }
47249
+ if (version > currentVersion) {
47250
+ throw new Error("\uC774 \uBC31\uC5C5\uC740 \uB354 \uCD5C\uC2E0 CareerMate\uC5D0\uC11C \uB9CC\uB4E4\uC5B4\uC84C\uC2B5\uB2C8\uB2E4. \uC571\uC744 \uC5C5\uB370\uC774\uD2B8\uD55C \uB4A4 \uB2E4\uC2DC \uAC00\uC838\uC640 \uC8FC\uC138\uC694.");
47251
+ }
47252
+ const allowedTables = new Set(listTables(db));
47253
+ const unknownTables = Object.keys(rawTables).filter((name) => !allowedTables.has(name));
47254
+ if (unknownTables.length > 0) {
47255
+ throw new Error(`\uD604\uC7AC \uC571\uC5D0\uC11C \uC54C \uC218 \uC5C6\uB294 \uB370\uC774\uD130\uAC00 \uD3EC\uD568\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4: ${unknownTables.join(", ")}`);
47256
+ }
47257
+ const columnsByTable = /* @__PURE__ */ new Map();
47258
+ for (const table of allowedTables) columnsByTable.set(table, new Set(tableColumns(db, table)));
47259
+ const tables = {};
47260
+ const counts = {};
47261
+ let totalRows = 0;
47262
+ for (const table of allowedTables) {
47263
+ const rows = rawTables[table] ?? [];
47264
+ if (!Array.isArray(rows)) throw new Error(`${table} \uB370\uC774\uD130\uAC00 \uBC30\uC5F4 \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4.`);
47265
+ const columns = columnsByTable.get(table);
47266
+ tables[table] = rows.map((row, rowIndex) => {
47267
+ if (!isRecord(row)) throw new Error(`${table} ${rowIndex + 1}\uBC88\uC9F8 \uD589\uC774 \uC62C\uBC14\uB978 \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4.`);
47268
+ const clean = {};
47269
+ for (const [key, value] of Object.entries(row)) {
47270
+ if (!columns.has(key)) throw new Error(`${table} \uB370\uC774\uD130\uC5D0 \uD604\uC7AC \uC571\uC5D0\uC11C \uC4F0\uC9C0 \uC54A\uB294 \uD56D\uBAA9(${key})\uC774 \uC788\uC2B5\uB2C8\uB2E4.`);
47271
+ if (value !== null && !["string", "number", "boolean"].includes(typeof value)) {
47272
+ throw new Error(`${table} \uB370\uC774\uD130\uC758 ${key} \uAC12 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`);
47273
+ }
47274
+ clean[key] = value;
47275
+ }
47276
+ if (Object.keys(clean).length === 0) throw new Error(`${table} ${rowIndex + 1}\uBC88\uC9F8 \uD589\uC774 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.`);
47277
+ return clean;
47278
+ });
47279
+ counts[table] = tables[table].length;
47280
+ totalRows += tables[table].length;
47281
+ }
47282
+ const warnings = [...allowedTables].filter((table) => !(table in rawTables)).map((table) => `${table} \uB370\uC774\uD130\uAC00 \uBC31\uC5C5 \uD30C\uC77C\uC5D0 \uC5C6\uC5B4 \uBE48 \uAC12\uC73C\uB85C \uAC00\uC838\uC635\uB2C8\uB2E4.`);
47283
+ return {
47284
+ exported_at: typeof raw.exported_at === "string" ? raw.exported_at : null,
47285
+ version,
47286
+ current_version: currentVersion,
47287
+ tables,
47288
+ counts,
47289
+ total_rows: totalRows,
47290
+ warnings
47291
+ };
47292
+ }
47293
+ function macLauncherScript() {
47294
+ const root2 = appRoot();
47295
+ const dataDir = getDataDir();
47296
+ const nodePath = process.execPath;
47297
+ const binPath = path4.join(root2, "bin", "careermate.mjs");
47298
+ const bundledWeb = BUNDLED ? path4.join(root2, "web.mjs") : path4.join(root2, "dist", "web.mjs");
47299
+ return `#!/bin/zsh
47300
+ set -e
47301
+
47302
+ export CAREERMATE_DATA_DIR=${shQuote(dataDir)}
47303
+ NODE=${shQuote(nodePath)}
47304
+ ROOT=${shQuote(root2)}
47305
+ BIN=${shQuote(binPath)}
47306
+ BUNDLED_WEB=${shQuote(bundledWeb)}
47307
+ INFO="$CAREERMATE_DATA_DIR/server.json"
47308
+
47309
+ if [ ! -x "$NODE" ]; then
47310
+ NODE="$(command -v node || true)"
47311
+ fi
47312
+ if [ -z "$NODE" ]; then
47313
+ echo "Node.js\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. CareerMate\uB97C \uC124\uCE58\uD55C \uB4A4 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC138\uC694."
47314
+ exit 1
47315
+ fi
47316
+
47317
+ URL=$("$NODE" -e 'const fs=require("fs"); try { const info=JSON.parse(fs.readFileSync(process.argv[1], "utf8")); process.stdout.write(info.url || ""); } catch {}' "$INFO")
47318
+ if [ -n "$URL" ] && command -v curl >/dev/null 2>&1 && curl -fsS --max-time 1 "$URL/api/health" >/dev/null 2>&1; then
47319
+ open "$URL"
47320
+ exit 0
47321
+ fi
47322
+
47323
+ echo "CareerMate \uB300\uC2DC\uBCF4\uB4DC\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4."
47324
+ if [ -f "$BIN" ]; then
47325
+ cd "$ROOT"
47326
+ exec "$NODE" "$BIN" start
47327
+ fi
47328
+
47329
+ if [ -f "$BUNDLED_WEB" ]; then
47330
+ cd "$(dirname "$BUNDLED_WEB")"
47331
+ exec "$NODE" --no-warnings --experimental-sqlite "$BUNDLED_WEB"
47332
+ fi
47333
+
47334
+ if command -v careermate >/dev/null 2>&1; then
47335
+ exec careermate start
47336
+ fi
47337
+
47338
+ exec npx -y careermate start
47339
+ `;
47340
+ }
47341
+ function windowsLauncherScript() {
47342
+ const root2 = appRoot();
47343
+ const dataDir = getDataDir();
47344
+ const nodePath = process.execPath;
47345
+ const binPath = path4.join(root2, "bin", "careermate.mjs");
47346
+ const bundledWeb = BUNDLED ? path4.join(root2, "web.mjs") : path4.join(root2, "dist", "web.mjs");
47347
+ return `@echo off
47348
+ setlocal
47349
+ set "CAREERMATE_DATA_DIR=${cmdQuote(dataDir)}"
47350
+ set "NODE=${cmdQuote(nodePath)}"
47351
+ set "ROOT=${cmdQuote(root2)}"
47352
+ set "BIN=${cmdQuote(binPath)}"
47353
+ set "BUNDLED_WEB=${cmdQuote(bundledWeb)}"
47354
+ set "INFO=%CAREERMATE_DATA_DIR%\\server.json"
47355
+ set "URL="
47356
+
47357
+ if not exist "%NODE%" (
47358
+ for /f "usebackq delims=" %%N in (\`where node 2^>nul\`) do (
47359
+ set "NODE=%%N"
47360
+ goto :node_found
47361
+ )
47362
+ )
47363
+ :node_found
47364
+ if not exist "%NODE%" (
47365
+ echo Node.js\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. CareerMate\uB97C \uC124\uCE58\uD55C \uB4A4 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC138\uC694.
47366
+ pause
47367
+ exit /b 1
47368
+ )
47369
+
47370
+ for /f "usebackq delims=" %%U in (\`"%NODE%" -e "const fs=require('fs'); try { const info=JSON.parse(fs.readFileSync(process.argv[1], 'utf8')); process.stdout.write(info.url || ''); } catch {}" "%INFO%"\`) do set "URL=%%U"
47371
+ if defined URL (
47372
+ powershell -NoProfile -ExecutionPolicy Bypass -Command "try { Invoke-WebRequest -UseBasicParsing -TimeoutSec 1 '%URL%/api/health' | Out-Null; Start-Process '%URL%'; exit 0 } catch { exit 1 }"
47373
+ if not errorlevel 1 exit /b 0
47374
+ )
47375
+
47376
+ echo CareerMate \uB300\uC2DC\uBCF4\uB4DC\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4.
47377
+ if exist "%BIN%" (
47378
+ cd /d "%ROOT%"
47379
+ "%NODE%" "%BIN%" start
47380
+ exit /b %ERRORLEVEL%
47381
+ )
47382
+
47383
+ if exist "%BUNDLED_WEB%" (
47384
+ cd /d "%ROOT%"
47385
+ "%NODE%" --no-warnings --experimental-sqlite "%BUNDLED_WEB%"
47386
+ exit /b %ERRORLEVEL%
47387
+ )
47388
+
47389
+ where careermate >nul 2>nul
47390
+ if not errorlevel 1 (
47391
+ careermate start
47392
+ exit /b %ERRORLEVEL%
47393
+ )
47394
+
47395
+ npx -y careermate start
47396
+ `;
47397
+ }
47398
+ function linuxLauncherScript() {
47399
+ return macLauncherScript().replace("#!/bin/zsh", "#!/usr/bin/env sh").replace('\n open "$URL"', '\n xdg-open "$URL"');
47400
+ }
46883
47401
  function exportAll() {
46884
47402
  const db = getDb();
46885
47403
  const tables = {};
46886
47404
  for (const t of listTables(db)) {
46887
- tables[t] = db.prepare(`SELECT * FROM ${t}`).all();
47405
+ tables[t] = db.prepare(`SELECT * FROM ${quoteIdent(t)}`).all();
46888
47406
  }
46889
- const ver = db.prepare(`SELECT value FROM _meta WHERE key='schema_version'`).get();
46890
- return { exported_at: (/* @__PURE__ */ new Date()).toISOString(), version: Number(ver?.value ?? 0), tables };
47407
+ return { exported_at: (/* @__PURE__ */ new Date()).toISOString(), version: schemaVersion(db), tables };
47408
+ }
47409
+ function previewBackupImport(raw) {
47410
+ const parsed = parseBackup(raw);
47411
+ return {
47412
+ exported_at: parsed.exported_at,
47413
+ version: parsed.version,
47414
+ current_version: parsed.current_version,
47415
+ counts: parsed.counts,
47416
+ total_rows: parsed.total_rows,
47417
+ warnings: parsed.warnings
47418
+ };
47419
+ }
47420
+ function restoreBackup(raw) {
47421
+ const parsed = parseBackup(raw);
47422
+ const safety = createBackup();
47423
+ const db = getDb();
47424
+ const tables = listTables(db);
47425
+ const restoreOrder = [
47426
+ ...RESTORE_ORDER.filter((table) => tables.includes(table)),
47427
+ ...tables.filter((table) => !RESTORE_ORDER.includes(table))
47428
+ ];
47429
+ tx(() => {
47430
+ for (const table of [...restoreOrder].reverse()) {
47431
+ db.prepare(`DELETE FROM ${quoteIdent(table)}`).run();
47432
+ }
47433
+ for (const table of restoreOrder) {
47434
+ for (const row of parsed.tables[table] ?? []) {
47435
+ const columns = Object.keys(row);
47436
+ const placeholders = columns.map(() => "?").join(",");
47437
+ const values2 = columns.map((column) => {
47438
+ const value = row[column];
47439
+ return typeof value === "boolean" ? Number(value) : value;
47440
+ });
47441
+ db.prepare(
47442
+ `INSERT INTO ${quoteIdent(table)} (${columns.map(quoteIdent).join(",")}) VALUES (${placeholders})`
47443
+ ).run(...values2);
47444
+ }
47445
+ }
47446
+ const fkProblems = db.prepare(`PRAGMA foreign_key_check`).all();
47447
+ if (fkProblems.length > 0) {
47448
+ throw new Error("\uBC31\uC5C5 \uD30C\uC77C\uC758 \uB370\uC774\uD130 \uAD00\uACC4\uAC00 \uB9DE\uC9C0 \uC54A\uC544 \uAC00\uC838\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
47449
+ }
47450
+ });
47451
+ return {
47452
+ ok: true,
47453
+ safety_backup_path: safety.backup_path,
47454
+ safety_json_path: safety.json_path,
47455
+ restored: previewBackupImport(raw)
47456
+ };
46891
47457
  }
46892
47458
  function createBackup() {
46893
47459
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
@@ -46908,6 +47474,38 @@ function createBackup() {
46908
47474
  fs4.writeFileSync(jsonBackup, JSON.stringify(exportAll(), null, 2), "utf8");
46909
47475
  return { backup_path: dbBackup, json_path: jsonBackup };
46910
47476
  }
47477
+ function createDashboardShortcut(open = false) {
47478
+ const dir = shortcutFolder();
47479
+ fs4.mkdirSync(dir, { recursive: true });
47480
+ const readme = `CareerMate \uB300\uC2DC\uBCF4\uB4DC \uBC14\uB85C\uAC00\uAE30
47481
+
47482
+ - \uB300\uC2DC\uBCF4\uB4DC\uAC00 \uC774\uBBF8 \uC2E4\uD589 \uC911\uC774\uBA74 \uBE0C\uB77C\uC6B0\uC800\uB9CC \uC5FD\uB2C8\uB2E4.
47483
+ - \uB300\uC2DC\uBCF4\uB4DC\uAC00 \uAEBC\uC838 \uC788\uC73C\uBA74 \uC11C\uBC84\uB97C \uC2DC\uC791\uD558\uACE0 \uBE0C\uB77C\uC6B0\uC800\uB97C \uC5FD\uB2C8\uB2E4.
47484
+ - \uC774 \uD3F4\uB354\uB294 \uC774 \uCEF4\uD4E8\uD130\uC758 CareerMate \uC124\uCE58 \uACBD\uB85C\uC640 \uB370\uC774\uD130 \uD3F4\uB354\uB97C \uAE30\uC900\uC73C\uB85C \uB9CC\uB4E4\uC5B4\uC84C\uC2B5\uB2C8\uB2E4.
47485
+ `;
47486
+ const readmePath = path4.join(dir, "README.txt");
47487
+ fs4.writeFileSync(readmePath, readme, "utf8");
47488
+ let launcherPath;
47489
+ if (process.platform === "win32") {
47490
+ launcherPath = path4.join(dir, "CareerMate \uB300\uC2DC\uBCF4\uB4DC.cmd");
47491
+ fs4.writeFileSync(launcherPath, windowsLauncherScript(), "utf8");
47492
+ } else if (process.platform === "darwin") {
47493
+ launcherPath = path4.join(dir, "CareerMate \uB300\uC2DC\uBCF4\uB4DC.command");
47494
+ fs4.writeFileSync(launcherPath, macLauncherScript(), { encoding: "utf8", mode: 493 });
47495
+ fs4.chmodSync(launcherPath, 493);
47496
+ } else {
47497
+ launcherPath = path4.join(dir, "careermate-dashboard.sh");
47498
+ fs4.writeFileSync(launcherPath, linuxLauncherScript(), { encoding: "utf8", mode: 493 });
47499
+ fs4.chmodSync(launcherPath, 493);
47500
+ }
47501
+ if (open) openFolder(dir);
47502
+ return {
47503
+ shortcut_dir: dir,
47504
+ launcher_path: launcherPath,
47505
+ platform: process.platform,
47506
+ files: [launcherPath, readmePath]
47507
+ };
47508
+ }
46911
47509
  function resetAll(confirm) {
46912
47510
  if (confirm !== "DELETE") {
46913
47511
  return { ok: false };
@@ -46916,7 +47514,7 @@ function resetAll(confirm) {
46916
47514
  const db = getDb();
46917
47515
  db.exec("BEGIN");
46918
47516
  try {
46919
- for (const t of listTables(db)) db.exec(`DELETE FROM ${t}`);
47517
+ for (const t of listTables(db)) db.exec(`DELETE FROM ${quoteIdent(t)}`);
46920
47518
  db.exec("COMMIT");
46921
47519
  } catch (e) {
46922
47520
  db.exec("ROLLBACK");
@@ -46944,6 +47542,141 @@ function fmt(ctx) {
46944
47542
  const f = (ctx.query.get("format") ?? "md").toLowerCase();
46945
47543
  return ["md", "html", "txt"].includes(f) ? f : "md";
46946
47544
  }
47545
+ function backupError(e) {
47546
+ return new HttpError(400, e instanceof Error ? e.message : "\uBC31\uC5C5 \uD30C\uC77C\uC744 \uCC98\uB9AC\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.", "backup_import");
47547
+ }
47548
+ function isRecord2(v) {
47549
+ return typeof v === "object" && v !== null && !Array.isArray(v);
47550
+ }
47551
+ function documentRoute(doc) {
47552
+ return doc.kind === "career_description" ? `/documents/career/${doc.id}` : `/documents/docs/${doc.id}`;
47553
+ }
47554
+ function enrichDocumentRef(ref) {
47555
+ if (!isRecord2(ref)) return ref;
47556
+ const refId = typeof ref.id === "string" ? ref.id : null;
47557
+ const doc = refId ? documentRepo.get(refId) : null;
47558
+ const kind = doc?.kind ?? ref.document_kind ?? ref.kind ?? null;
47559
+ return {
47560
+ ...ref,
47561
+ title: doc?.title ?? ref.title ?? "\uC0AD\uC81C\uB41C \uBB38\uC11C",
47562
+ document_kind: kind,
47563
+ exists: !!doc,
47564
+ route: doc ? documentRoute(doc) : null
47565
+ };
47566
+ }
47567
+ function enrichCoverLetterRef(ref) {
47568
+ if (!isRecord2(ref)) return ref;
47569
+ const refId = typeof ref.id === "string" ? ref.id : null;
47570
+ const cl = refId ? coverLetterRepo.get(refId, false) : null;
47571
+ return {
47572
+ ...ref,
47573
+ title: cl?.title ?? ref.title ?? "\uC0AD\uC81C\uB41C \uC790\uAE30\uC18C\uAC1C\uC11C",
47574
+ exists: !!cl,
47575
+ route: cl ? `/documents/cover/${cl.id}` : null
47576
+ };
47577
+ }
47578
+ function enrichTimelinePayload(payload) {
47579
+ const out = isRecord2(payload) ? { ...payload } : {};
47580
+ if (out.cover_letter) out.cover_letter = enrichCoverLetterRef(out.cover_letter);
47581
+ if (isRecord2(out.submission)) {
47582
+ const submission = { ...out.submission };
47583
+ if (submission.cover_letter) submission.cover_letter = enrichCoverLetterRef(submission.cover_letter);
47584
+ if (Array.isArray(submission.documents)) submission.documents = submission.documents.map(enrichDocumentRef);
47585
+ out.submission = submission;
47586
+ }
47587
+ return out;
47588
+ }
47589
+ function buildTimeline(job) {
47590
+ const application = applicationRepo.getByJob(job.id);
47591
+ const fit = fitRepo.getByJob(job.id);
47592
+ const coverLetters = coverLetterRepo.listByJob(job.id);
47593
+ const interview = interviewRepo.getByJob(job.id);
47594
+ const stored = timelineRepo.listByJob(job.id);
47595
+ const events = [
47596
+ {
47597
+ id: `synthetic-job-${job.id}`,
47598
+ job_id: job.id,
47599
+ type: "job_saved",
47600
+ title: "\uACF5\uACE0 \uB4F1\uB85D",
47601
+ summary: `${job.company} \xB7 ${job.position}`,
47602
+ payload: { synthetic: true },
47603
+ occurred_at: job.created_at,
47604
+ created_at: job.created_at
47605
+ },
47606
+ ...stored
47607
+ ];
47608
+ const hasStored = (type, predicate) => stored.some((event) => event.type === type && (!predicate || predicate(enrichTimelinePayload(event.payload))));
47609
+ if (fit && !hasStored("fit_analysis_saved", (payload) => payload.fit_id === fit.id)) {
47610
+ events.push({
47611
+ id: `synthetic-fit-${fit.id}`,
47612
+ job_id: job.id,
47613
+ type: "fit_analysis_saved",
47614
+ title: "\uC801\uD569\uB3C4 \uBD84\uC11D",
47615
+ summary: fit.score != null ? `\uC885\uD569 ${fit.score}\uC810` : fit.summary,
47616
+ payload: { synthetic: true, fit_id: fit.id, score: fit.score },
47617
+ occurred_at: fit.updated_at,
47618
+ created_at: fit.created_at
47619
+ });
47620
+ }
47621
+ for (const cl of coverLetters) {
47622
+ const versionId = cl.current_version_id;
47623
+ const alreadyStored = hasStored("cover_letter_version_saved", (payload) => {
47624
+ const ref = isRecord2(payload.cover_letter) ? payload.cover_letter : null;
47625
+ return ref?.id === cl.id && (!versionId || ref.version_id === versionId);
47626
+ });
47627
+ if (!alreadyStored) {
47628
+ events.push({
47629
+ id: `synthetic-cover-${cl.id}`,
47630
+ job_id: job.id,
47631
+ type: "cover_letter_version_saved",
47632
+ title: "\uC790\uAE30\uC18C\uAC1C\uC11C \uC791\uC131",
47633
+ summary: `${cl.title}${cl.version_count ? ` v${cl.version_count}` : ""}`,
47634
+ payload: {
47635
+ synthetic: true,
47636
+ cover_letter: {
47637
+ kind: "cover_letter",
47638
+ id: cl.id,
47639
+ title: cl.title,
47640
+ version_id: versionId,
47641
+ version_no: cl.version_count
47642
+ }
47643
+ },
47644
+ occurred_at: cl.updated_at,
47645
+ created_at: cl.created_at
47646
+ });
47647
+ }
47648
+ }
47649
+ if (application?.status && application.status !== "draft" && !hasStored("application_status_changed", (payload) => payload.status === application.status)) {
47650
+ events.push({
47651
+ id: `synthetic-status-${application.id}`,
47652
+ job_id: job.id,
47653
+ type: "application_status_changed",
47654
+ title: APPLICATION_STATUS_LABELS[application.status],
47655
+ summary: application.notes,
47656
+ payload: {
47657
+ synthetic: true,
47658
+ status: application.status,
47659
+ status_label: APPLICATION_STATUS_LABELS[application.status],
47660
+ submission: null
47661
+ },
47662
+ occurred_at: application.applied_at ?? application.updated_at,
47663
+ created_at: application.created_at
47664
+ });
47665
+ }
47666
+ if (interview && !hasStored("interview_prep_saved", (payload) => payload.interview_prep_id === interview.id)) {
47667
+ events.push({
47668
+ id: `synthetic-interview-${interview.id}`,
47669
+ job_id: job.id,
47670
+ type: "interview_prep_saved",
47671
+ title: "\uBA74\uC811 \uC900\uBE44",
47672
+ summary: interview.questions.length > 0 ? `\uC608\uC0C1 \uC9C8\uBB38 ${interview.questions.length}\uAC1C` : interview.notes,
47673
+ payload: { synthetic: true, interview_prep_id: interview.id, question_count: interview.questions.length },
47674
+ occurred_at: interview.updated_at,
47675
+ created_at: interview.created_at
47676
+ });
47677
+ }
47678
+ return events.sort((a, b) => String(a.occurred_at).localeCompare(String(b.occurred_at)) || String(a.created_at).localeCompare(String(b.created_at))).map((event) => ({ ...event, payload: enrichTimelinePayload(event.payload) }));
47679
+ }
46947
47680
  function jobDetail(jobId) {
46948
47681
  const job = jobRepo.get(jobId);
46949
47682
  if (!job) throw new HttpError(404, "\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
@@ -46952,13 +47685,15 @@ function jobDetail(jobId) {
46952
47685
  const cover_letters = coverLetterRepo.listByJob(jobId);
46953
47686
  const interview = interviewRepo.getByJob(jobId);
46954
47687
  const related = jobRepo.relatedTo(job.company, job.position, job.id).map(jobWithMeta);
47688
+ const timeline = buildTimeline(job);
46955
47689
  return {
46956
47690
  ...jobWithMeta(job),
46957
47691
  application,
46958
47692
  fit,
46959
47693
  cover_letters,
46960
47694
  interview,
46961
- related
47695
+ related,
47696
+ timeline
46962
47697
  };
46963
47698
  }
46964
47699
  function registerApiRoutes(router2) {
@@ -47107,12 +47842,12 @@ function registerApiRoutes(router2) {
47107
47842
  });
47108
47843
  router2.get("/api/interview", () => {
47109
47844
  const preps = interviewRepo.list().map((p) => ({ ...p, job: jobRepo.get(p.job_id) }));
47110
- const eligible = applicationRepo.list().filter((a) => INTERVIEW_UNLOCK_STATUSES.includes(a.status)).map((a) => ({
47845
+ const eligible = applicationRepo.list().map((a) => ({
47111
47846
  job: jobRepo.get(a.job_id),
47112
47847
  status: a.status,
47113
47848
  status_label: APPLICATION_STATUS_LABELS[a.status],
47114
47849
  has_prep: !!interviewRepo.getByJob(a.job_id)
47115
- })).filter((x) => x.job);
47850
+ })).filter((x) => x.job && (x.has_prep || INTERVIEW_UNLOCK_STATUSES.includes(x.status)));
47116
47851
  return { preps, eligible };
47117
47852
  });
47118
47853
  router2.get("/api/jobs/:id/interview", (ctx) => ({ interview: interviewRepo.getByJob(id2(ctx)) }));
@@ -47131,7 +47866,7 @@ function registerApiRoutes(router2) {
47131
47866
  });
47132
47867
  router2.put("/api/applications/:jobId/status", async (ctx) => {
47133
47868
  const body = await readJsonBody(ctx.req, ApplicationStatusUpdateSchema.omit({ job_id: true, application_id: true }));
47134
- return updateApplicationStatus(id2(ctx, "jobId"), body.status, body.note);
47869
+ return updateApplicationStatus(id2(ctx, "jobId"), body.status, body.note, body.submission);
47135
47870
  });
47136
47871
  router2.put("/api/applications/:jobId", async (ctx) => {
47137
47872
  const body = await readJsonBody(ctx.req, ApplicationInputSchema.omit({ job_id: true }));
@@ -47160,6 +47895,32 @@ function registerApiRoutes(router2) {
47160
47895
  return { verify_strict: on };
47161
47896
  });
47162
47897
  router2.post("/api/settings/backup", () => createBackup());
47898
+ router2.post("/api/settings/dashboard-shortcut", async (ctx) => {
47899
+ const { open } = await readJsonBody(ctx.req, external_exports.object({ open: external_exports.boolean().optional() }));
47900
+ return { shortcut: createDashboardShortcut(open === true) };
47901
+ });
47902
+ router2.post("/api/settings/import-preview", async (ctx) => {
47903
+ const { backup } = await readJsonBody(ctx.req, external_exports.object({ backup: external_exports.unknown() }));
47904
+ try {
47905
+ return { preview: previewBackupImport(backup) };
47906
+ } catch (e) {
47907
+ throw backupError(e);
47908
+ }
47909
+ });
47910
+ router2.post("/api/settings/restore", async (ctx) => {
47911
+ const { backup, confirm } = await readJsonBody(
47912
+ ctx.req,
47913
+ external_exports.object({ backup: external_exports.unknown(), confirm: external_exports.string() })
47914
+ );
47915
+ if (confirm !== "RESTORE") {
47916
+ throw new HttpError(400, "\uBCF5\uC6D0\uC744 \uC9C4\uD589\uD558\uB824\uBA74 \uD655\uC778 \uAC12\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.", "restore_confirm_required");
47917
+ }
47918
+ try {
47919
+ return restoreBackup(backup);
47920
+ } catch (e) {
47921
+ throw backupError(e);
47922
+ }
47923
+ });
47163
47924
  router2.post("/api/settings/reset", async (ctx) => {
47164
47925
  const { confirm } = await readJsonBody(ctx.req, external_exports.object({ confirm: external_exports.string() }));
47165
47926
  const result2 = resetAll(confirm);
@@ -47187,9 +47948,9 @@ function isDownload(v) {
47187
47948
  }
47188
47949
 
47189
47950
  // apps/web/src/server.ts
47190
- var __dirname = path5.dirname(fileURLToPath(import.meta.url));
47191
- var WEB_ROOT = BUNDLED ? path5.resolve(__dirname, "public") : path5.resolve(__dirname, "..", "public");
47192
- var INSTALL_ROOT = BUNDLED ? path5.resolve(__dirname, "site") : path5.resolve(__dirname, "..", "..", "..", "site");
47951
+ var __dirname2 = path5.dirname(fileURLToPath2(import.meta.url));
47952
+ var WEB_ROOT = BUNDLED ? path5.resolve(__dirname2, "public") : path5.resolve(__dirname2, "..", "public");
47953
+ var LANDING_URL = "https://careermate.life/";
47193
47954
  var router = new Router();
47194
47955
  registerApiRoutes(router);
47195
47956
  function createServer() {
@@ -47224,16 +47985,11 @@ function createServer() {
47224
47985
  }
47225
47986
  return;
47226
47987
  }
47227
- if (pathname === "/install") {
47228
- res.writeHead(308, { Location: `/install/${url.search}` });
47988
+ if (pathname === "/install" || pathname.startsWith("/install/")) {
47989
+ res.writeHead(302, { Location: LANDING_URL });
47229
47990
  res.end();
47230
47991
  return;
47231
47992
  }
47232
- if (pathname.startsWith("/install/")) {
47233
- const rel2 = pathname === "/install/" ? "index.html" : pathname.slice("/install/".length);
47234
- if (serveStatic(res, INSTALL_ROOT, rel2)) return;
47235
- if (serveStatic(res, INSTALL_ROOT, "index.html")) return;
47236
- }
47237
47993
  const rel = pathname === "/" ? "index.html" : pathname.slice(1);
47238
47994
  if (serveStatic(res, WEB_ROOT, rel, injectToken)) return;
47239
47995
  if (serveStatic(res, WEB_ROOT, "index.html", injectToken)) return;
@@ -47291,11 +48047,11 @@ function openBrowser(url) {
47291
48047
  const platform = process.platform;
47292
48048
  try {
47293
48049
  if (platform === "win32") {
47294
- spawn("cmd", ["/c", "start", "", url], { stdio: "ignore", detached: true }).unref();
48050
+ spawn2("cmd", ["/c", "start", "", url], { stdio: "ignore", detached: true }).unref();
47295
48051
  } else if (platform === "darwin") {
47296
- spawn("open", [url], { stdio: "ignore", detached: true }).unref();
48052
+ spawn2("open", [url], { stdio: "ignore", detached: true }).unref();
47297
48053
  } else {
47298
- spawn("xdg-open", [url], { stdio: "ignore", detached: true }).unref();
48054
+ spawn2("xdg-open", [url], { stdio: "ignore", detached: true }).unref();
47299
48055
  }
47300
48056
  } catch {
47301
48057
  }
@@ -47308,7 +48064,7 @@ async function main() {
47308
48064
  console.log(` CareerMate v${APP_VERSION} \u2014 \uB0B4 \uCEE4\uB9AC\uC5B4 \uD750\uB984 \uAD00\uB9AC \uB3C4\uAD6C`);
47309
48065
  console.log(`${" ".repeat(2)}${"\u2504".repeat(52)}`);
47310
48066
  console.log(` \uB300\uC2DC\uBCF4\uB4DC ${url}`);
47311
- console.log(` \uC124\uCE58 \uC548\uB0B4 ${url}/install`);
48067
+ console.log(` \uC124\uCE58\xB7\uC548\uB0B4 https://careermate.life`);
47312
48068
  console.log(` \uB370\uC774\uD130 \uC704\uCE58 ${getDataDir()}`);
47313
48069
  console.log(` (\uBAA8\uB4E0 \uB370\uC774\uD130\uB294 \uC774 \uCEF4\uD4E8\uD130\uC5D0\uB9CC \uC800\uC7A5\uB429\uB2C8\uB2E4)`);
47314
48070
  console.log(`\u2514${line}\u2518