careermate 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/doctor.mjs CHANGED
@@ -68172,7 +68172,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
68172
68172
  const ij = i2 * width + j;
68173
68173
  const center = buf[ij];
68174
68174
  let sum = 0;
68175
- let norm = 0;
68175
+ let norm2 = 0;
68176
68176
  for (let k = 0; k < kernelSize; k++) {
68177
68177
  const y = i2 + k - halfSize;
68178
68178
  if (y < 0 || y >= height) {
@@ -68186,10 +68186,10 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
68186
68186
  const neighbour = buf[y * width + x2];
68187
68187
  const w = kernel[k * kernelSize + l2] * rangeValues[Math.abs(neighbour - center)];
68188
68188
  sum += neighbour * w;
68189
- norm += w;
68189
+ norm2 += w;
68190
68190
  }
68191
68191
  }
68192
- const pix = out[ij] = Math.round(sum / norm);
68192
+ const pix = out[ij] = Math.round(sum / norm2);
68193
68193
  histogram[pix]++;
68194
68194
  }
68195
68195
  }
@@ -71451,6 +71451,24 @@ function setVerifyStrict(on) {
71451
71451
  return on;
71452
71452
  }
71453
71453
 
71454
+ // packages/shared/src/errors.ts
71455
+ var CareerMateError = class extends Error {
71456
+ constructor(message, httpStatus = 400, code) {
71457
+ super(message);
71458
+ this.httpStatus = httpStatus;
71459
+ this.code = code;
71460
+ this.name = "CareerMateError";
71461
+ }
71462
+ httpStatus;
71463
+ code;
71464
+ };
71465
+ function notFound(message) {
71466
+ return new CareerMateError(message, 404, "not_found");
71467
+ }
71468
+ function conflict(message) {
71469
+ return new CareerMateError(message, 409, "conflict");
71470
+ }
71471
+
71454
71472
  // packages/shared/src/enums.ts
71455
71473
  var APPLICATION_STATUSES = [
71456
71474
  "draft",
@@ -71501,8 +71519,9 @@ function canTransitionStatus(from, to) {
71501
71519
  }
71502
71520
  function assertStatusTransition(from, to) {
71503
71521
  if (!canTransitionStatus(from, to)) {
71504
- throw new Error(
71505
- `'${APPLICATION_STATUS_LABELS[from]}'\uC5D0\uC11C '${APPLICATION_STATUS_LABELS[to]}'(\uC73C)\uB85C\uB294 \uC0C1\uD0DC\uB97C \uBC14\uAFC0 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`
71522
+ const suffix = to === "draft" ? ` ('${APPLICATION_STATUS_LABELS[from]}' \uB2E8\uACC4\uC758 \uC9C0\uC6D0\uC740 \uC791\uC131 \uC911(\uBBF8\uCD94\uC801)\uC73C\uB85C \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uCD08\uAE30 \uB2E8\uACC4\uB85C \uC62E\uAE30\uB824\uBA74 '\uC9C0\uC6D0 \uC608\uC815'\uC744 \uC120\uD0DD\uD558\uC138\uC694.)` : "";
71523
+ throw conflict(
71524
+ `'${APPLICATION_STATUS_LABELS[from]}'\uC5D0\uC11C '${APPLICATION_STATUS_LABELS[to]}'(\uC73C)\uB85C\uB294 \uC0C1\uD0DC\uB97C \uBC14\uAFC0 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.${suffix}`
71506
71525
  );
71507
71526
  }
71508
71527
  }
@@ -75595,7 +75614,20 @@ var optLine = external_exports.string().max(MAX_LINE).optional();
75595
75614
  var optNote = external_exports.string().max(MAX_NOTE).optional();
75596
75615
  var optBody = external_exports.string().max(MAX_BODY).optional();
75597
75616
  var reqBody = external_exports.string().max(MAX_BODY).refine((s) => s.trim().length > 0, { message: EMPTY_MSG });
75598
- var strList = external_exports.array(external_exports.string().max(MAX_ITEM)).max(MAX_ITEMS);
75617
+ function toStrList(v) {
75618
+ if (typeof v !== "string") return v;
75619
+ const s = v.trim();
75620
+ if (!s) return [];
75621
+ if (s.startsWith("[")) {
75622
+ try {
75623
+ const parsed = JSON.parse(s);
75624
+ if (Array.isArray(parsed)) return parsed;
75625
+ } catch {
75626
+ }
75627
+ }
75628
+ return s.split(/[\n,]/).map((x2) => x2.trim()).filter(Boolean);
75629
+ }
75630
+ var strList = external_exports.preprocess(toStrList, external_exports.array(external_exports.string().max(MAX_ITEM)).max(MAX_ITEMS));
75599
75631
  var baseRecord = {
75600
75632
  id,
75601
75633
  created_at: isoDateTime,
@@ -75657,6 +75689,9 @@ var ExperienceRecordSchema = external_exports.object({
75657
75689
  tech: external_exports.array(external_exports.string()),
75658
75690
  order_index: external_exports.number()
75659
75691
  });
75692
+ var ExperienceBatchInputSchema = external_exports.object({
75693
+ experiences: external_exports.array(ExperienceInputSchema).min(1).max(MAX_ITEMS).describe("\uCD94\uAC00\uD560 \uACBD\uB825 \uBAA9\uB85D(\uD55C \uAC74\uC774\uC5B4\uB3C4 \uBC30\uC5F4\uB85C). \uAC19\uC740 \uD68C\uC0AC\xB7\uC9C1\uBB34\xB7\uC785\uC0AC\uC77C\uC740 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.")
75694
+ });
75660
75695
  var ProjectInputSchema = external_exports.object({
75661
75696
  name: reqLine,
75662
75697
  role: optLine,
@@ -75680,6 +75715,9 @@ var ProjectRecordSchema = external_exports.object({
75680
75715
  end_date: external_exports.string().nullable(),
75681
75716
  order_index: external_exports.number()
75682
75717
  });
75718
+ var ProjectBatchInputSchema = external_exports.object({
75719
+ projects: external_exports.array(ProjectInputSchema).min(1).max(MAX_ITEMS).describe("\uCD94\uAC00\uD560 \uD504\uB85C\uC81D\uD2B8 \uBAA9\uB85D(\uD55C \uAC1C\uC5EC\uB3C4 \uBC30\uC5F4\uB85C). \uAC19\uC740 \uC774\uB984\uC758 \uD504\uB85C\uC81D\uD2B8\uB294 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.")
75720
+ });
75683
75721
  var SkillInputSchema = external_exports.object({
75684
75722
  name: reqLine,
75685
75723
  category: external_exports.string().max(MAX_LINE).optional().describe("\uC5B8\uC5B4/\uD504\uB808\uC784\uC6CC\uD06C/\uD234/\uC18C\uD504\uD2B8\uC2A4\uD0AC \uB4F1"),
@@ -75695,6 +75733,9 @@ var SkillRecordSchema = external_exports.object({
75695
75733
  years: external_exports.number().nullable(),
75696
75734
  order_index: external_exports.number()
75697
75735
  });
75736
+ var SkillBatchInputSchema = external_exports.object({
75737
+ skills: external_exports.array(SkillInputSchema).min(1).max(MAX_ITEMS).describe("\uCD94\uAC00\uD560 \uAE30\uC220\uC2A4\uD0DD \uBAA9\uB85D(\uD55C \uAC1C\uC5EC\uB3C4 \uBC30\uC5F4\uB85C). \uAC19\uC740 \uC774\uB984\uC758 \uAE30\uC220\uC740 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.")
75738
+ });
75698
75739
  var DocumentInputSchema = external_exports.object({
75699
75740
  kind: external_exports.enum(DOCUMENT_KINDS),
75700
75741
  title: reqLine,
@@ -75891,7 +75932,7 @@ var ApplicationContextSchema = external_exports.object({
75891
75932
  });
75892
75933
 
75893
75934
  // packages/shared/src/build-info.ts
75894
- var APP_VERSION = true ? "0.0.3" : "0.0.0-dev";
75935
+ var APP_VERSION = true ? "0.0.4" : "0.0.0-dev";
75895
75936
  var BUNDLED = true;
75896
75937
 
75897
75938
  // packages/shared/src/index.ts
@@ -75906,6 +75947,7 @@ function now() {
75906
75947
 
75907
75948
  // packages/db/src/repositories.ts
75908
75949
  var PROFILE_ID = "profile-singleton";
75950
+ var norm = (s) => (s ?? "").trim().toLowerCase();
75909
75951
  function mapProfile(r) {
75910
75952
  return {
75911
75953
  id: r.id,
@@ -75983,6 +76025,9 @@ function mapExperience(r) {
75983
76025
  updated_at: r.updated_at
75984
76026
  };
75985
76027
  }
76028
+ function expKey(company, role, start) {
76029
+ return [norm(company), norm(role), norm(start)].join("\0");
76030
+ }
75986
76031
  var experienceRepo = {
75987
76032
  list() {
75988
76033
  return getDb().prepare(`SELECT * FROM experiences ORDER BY order_index ASC, start_date DESC`).all().map(mapExperience);
@@ -75991,6 +76036,32 @@ var experienceRepo = {
75991
76036
  const r = getDb().prepare(`SELECT * FROM experiences WHERE id = ?`).get(id2);
75992
76037
  return r ? mapExperience(r) : null;
75993
76038
  },
76039
+ /** Existing experience with the same company+role+start_date, or null (for idempotent batch upserts). */
76040
+ findMatch(input) {
76041
+ const key = expKey(input.company, input.role ?? null, input.start_date ?? null);
76042
+ return this.list().find((e) => expKey(e.company, e.role, e.start_date) === key) ?? null;
76043
+ },
76044
+ /** Insert, or merge into the matching experience (omitted fields keep their stored value). */
76045
+ upsert(input) {
76046
+ const existing = this.findMatch(input);
76047
+ if (existing) return { record: this.update(existing.id, input), created: false };
76048
+ return { record: this.add(input), created: true };
76049
+ },
76050
+ /** Batch insert/merge in one transaction; within-batch duplicates collapse onto one row. */
76051
+ addMany(inputs) {
76052
+ return tx(() => {
76053
+ const records = [];
76054
+ let created = 0;
76055
+ let updated = 0;
76056
+ for (const input of inputs) {
76057
+ const { record, created: isNew } = this.upsert(input);
76058
+ records.push(record);
76059
+ if (isNew) created += 1;
76060
+ else updated += 1;
76061
+ }
76062
+ return { records, created, updated };
76063
+ });
76064
+ },
75994
76065
  add(input) {
75995
76066
  const ts = now();
75996
76067
  const id2 = newId("exp_");
@@ -76064,6 +76135,32 @@ var projectRepo = {
76064
76135
  const r = getDb().prepare(`SELECT * FROM projects WHERE id = ?`).get(id2);
76065
76136
  return r ? mapProject(r) : null;
76066
76137
  },
76138
+ /** Existing project with the same name (case-insensitive), or null (for idempotent batch upserts). */
76139
+ findByName(name) {
76140
+ const key = norm(name);
76141
+ return this.list().find((p) => norm(p.name) === key) ?? null;
76142
+ },
76143
+ /** Insert, or merge into the same-named project (omitted fields keep their stored value). */
76144
+ upsert(input) {
76145
+ const existing = this.findByName(input.name);
76146
+ if (existing) return { record: this.update(existing.id, input), created: false };
76147
+ return { record: this.add(input), created: true };
76148
+ },
76149
+ /** Batch insert/merge in one transaction; within-batch duplicates collapse onto one row. */
76150
+ addMany(inputs) {
76151
+ return tx(() => {
76152
+ const records = [];
76153
+ let created = 0;
76154
+ let updated = 0;
76155
+ for (const input of inputs) {
76156
+ const { record, created: isNew } = this.upsert(input);
76157
+ records.push(record);
76158
+ if (isNew) created += 1;
76159
+ else updated += 1;
76160
+ }
76161
+ return { records, created, updated };
76162
+ });
76163
+ },
76067
76164
  add(input) {
76068
76165
  const ts = now();
76069
76166
  const id2 = newId("prj_");
@@ -76131,6 +76228,32 @@ var skillRepo = {
76131
76228
  const r = getDb().prepare(`SELECT * FROM skills WHERE id = ?`).get(id2);
76132
76229
  return r ? mapSkill(r) : null;
76133
76230
  },
76231
+ /** Existing skill with the same name (case-insensitive), or null (for idempotent batch upserts). */
76232
+ findByName(name) {
76233
+ const key = norm(name);
76234
+ return this.list().find((s) => norm(s.name) === key) ?? null;
76235
+ },
76236
+ /** Insert, or merge into the same-named skill (omitted fields keep their stored value). */
76237
+ upsert(input) {
76238
+ const existing = this.findByName(input.name);
76239
+ if (existing) return { record: this.update(existing.id, input), created: false };
76240
+ return { record: this.add(input), created: true };
76241
+ },
76242
+ /** Batch insert/merge in one transaction; within-batch duplicates collapse onto one row. */
76243
+ addMany(inputs) {
76244
+ return tx(() => {
76245
+ const records = [];
76246
+ let created = 0;
76247
+ let updated = 0;
76248
+ for (const input of inputs) {
76249
+ const { record, created: isNew } = this.upsert(input);
76250
+ records.push(record);
76251
+ if (isNew) created += 1;
76252
+ else updated += 1;
76253
+ }
76254
+ return { records, created, updated };
76255
+ });
76256
+ },
76134
76257
  add(input) {
76135
76258
  const ts = now();
76136
76259
  const id2 = newId("skl_");
@@ -76960,7 +77083,7 @@ function styleSignals(text) {
76960
77083
  }
76961
77084
  var DISCLAIMER = "\uC774 \uC810\uAC80\uC740 LLM \uC5C6\uC774 \uC140 \uC218 \uC788\uB294 \uAC83\uB9CC \uBD05\uB2C8\uB2E4(\uC22B\uC790 \uCD9C\uCC98\xB7\uBB38\uCCB4 \uC2E0\uD638). \uD1B5\uACFC=\uD569\uACA9\uC774 \uC544\uB2C8\uBA70, \uC0AC\uC2E4\xB7\uB3D9\uBB38\uC11C\uB2F5\xB7\uC758\uBBF8 \uD310\uB2E8\uC740 \uB2F9\uC2E0(AI)\uC774 \uC9C1\uC811 \uD574\uC57C \uD569\uB2C8\uB2E4.";
76962
77085
  function lintArtifact(kind, text, corpus, opts = {}) {
76963
- const norm = normalizeText(text);
77086
+ const norm2 = normalizeText(text);
76964
77087
  const corpusAvailable = (corpus.documents + corpus.structured).trim().length > 0;
76965
77088
  const provenance = analyzeProvenance(text, corpus);
76966
77089
  const signals = styleSignals(text);
@@ -76984,7 +77107,7 @@ function lintArtifact(kind, text, corpus, opts = {}) {
76984
77107
  }
76985
77108
  return {
76986
77109
  kind,
76987
- charCount: [...norm].length,
77110
+ charCount: [...norm2].length,
76988
77111
  signals,
76989
77112
  provenance,
76990
77113
  blocking,
@@ -77044,20 +77167,40 @@ function addResume(input) {
77044
77167
  activityRepo.log("resume_added", `${doc.title} \uBB38\uC11C\uB97C \uCD94\uAC00\uD588\uC2B5\uB2C8\uB2E4.`, "document", doc.id);
77045
77168
  return doc;
77046
77169
  }
77047
- function addExperience(input) {
77048
- const exp = experienceRepo.add(input);
77049
- activityRepo.log("profile_updated", `\uACBD\uB825(${exp.company})\uC744 \uCD94\uAC00\uD588\uC2B5\uB2C8\uB2E4.`, "experience", exp.id);
77050
- return exp;
77170
+ function batchNames(names) {
77171
+ const shown = names.filter(Boolean).slice(0, 8).join(", ");
77172
+ if (!shown) return "";
77173
+ return `: ${shown}${names.length > 8 ? " \uC678" : ""}`;
77051
77174
  }
77052
- function addProject(input) {
77053
- const prj = projectRepo.add(input);
77054
- activityRepo.log("profile_updated", `\uD504\uB85C\uC81D\uD2B8(${prj.name})\uB97C \uCD94\uAC00\uD588\uC2B5\uB2C8\uB2E4.`, "project", prj.id);
77055
- return prj;
77175
+ function addExperiences(inputs) {
77176
+ const res = experienceRepo.addMany(inputs);
77177
+ activityRepo.log(
77178
+ "profile_updated",
77179
+ `\uACBD\uB825 ${res.records.length}\uAC74\uC744 \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4(\uC2E0\uADDC ${res.created} \xB7 \uAC31\uC2E0 ${res.updated})${batchNames(res.records.map((e) => e.company))}.`,
77180
+ "experience",
77181
+ res.records[0]?.id
77182
+ );
77183
+ return res;
77184
+ }
77185
+ function addProjects(inputs) {
77186
+ const res = projectRepo.addMany(inputs);
77187
+ activityRepo.log(
77188
+ "profile_updated",
77189
+ `\uD504\uB85C\uC81D\uD2B8 ${res.records.length}\uAC74\uC744 \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4(\uC2E0\uADDC ${res.created} \xB7 \uAC31\uC2E0 ${res.updated})${batchNames(res.records.map((p) => p.name))}.`,
77190
+ "project",
77191
+ res.records[0]?.id
77192
+ );
77193
+ return res;
77056
77194
  }
77057
- function addSkill(input) {
77058
- const skill = skillRepo.add(input);
77059
- activityRepo.log("profile_updated", `\uAE30\uC220(${skill.name})\uC744 \uCD94\uAC00\uD588\uC2B5\uB2C8\uB2E4.`, "skill", skill.id);
77060
- return skill;
77195
+ function addSkills(inputs) {
77196
+ const res = skillRepo.addMany(inputs);
77197
+ activityRepo.log(
77198
+ "profile_updated",
77199
+ `\uAE30\uC220 ${res.records.length}\uAC1C\uB97C \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4(\uC2E0\uADDC ${res.created} \xB7 \uAC31\uC2E0 ${res.updated})${batchNames(res.records.map((s) => s.name))}.`,
77200
+ "skill",
77201
+ res.records[0]?.id
77202
+ );
77203
+ return res;
77061
77204
  }
77062
77205
  function saveJobPosting(input, id2) {
77063
77206
  const job = jobRepo.upsert(input, id2);
@@ -77067,7 +77210,7 @@ function saveJobPosting(input, id2) {
77067
77210
  }
77068
77211
  function saveFitAnalysis(input) {
77069
77212
  const job = jobRepo.get(input.job_id);
77070
- if (!job) throw new Error(`\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.`);
77213
+ if (!job) throw notFound(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.job_id}. \uBA3C\uC800 save_job_posting\uC73C\uB85C \uACF5\uACE0\uB97C \uC800\uC7A5\uD558\uC138\uC694.`);
77071
77214
  applicationRepo.ensure(job.id, "draft");
77072
77215
  const fit = fitRepo.save(input);
77073
77216
  const scoreText = fit.score != null ? ` (\uC801\uD569\uB3C4 ${fit.score}\uC810)` : "";
@@ -77082,7 +77225,7 @@ function saveCoverLetterVersion(input) {
77082
77225
  const detail = verification.blocking.map((b) => `${b.label}: ${b.detail}`).join("; ");
77083
77226
  const strictHit = verification.blocking.some((b) => b.id === "unverified_numbers_strict");
77084
77227
  const tail = strictHit ? `\uC5C4\uACA9 \uBAA8\uB4DC\uAC00 \uCF1C\uC838 \uC788\uC5B4, \uC62C\uB9B0 \uC774\uB825\uC11C \uBCF8\uBB38\uC5D0 \uC5C6\uB294 \uC218\uCE58\uB294 \uB9C9\uD799\uB2C8\uB2E4. \uC774\uB825\uC11C\uB97C \uC62C\uB9AC\uAC70\uB098 \uC5C4\uACA9 \uBAA8\uB4DC\uB97C \uB044\uAC70\uB098, \uC758\uB3C4\uD55C \uAC12\uC774\uBA74 force: true\uB85C \uC800\uC7A5\uD558\uC138\uC694.` : `\uC774 \uC218\uCE58\uB294 \uC800\uC7A5\uB41C \uACBD\uB825\xB7\uC774\uB825\uC11C\xB7\uD504\uB85C\uC81D\uD2B8\uC5D0 \uC5C6\uC2B5\uB2C8\uB2E4. \uC6D0\uBCF8 \uB370\uC774\uD130\uC758 \uC2E4\uC81C \uC218\uCE58\uB85C \uACE0\uCE58\uAC70\uB098, \uC758\uB3C4\uD55C \uAC12\uC774 \uB9DE\uB2E4\uBA74 \uBA3C\uC800 \uACBD\uB825/\uD504\uB85C\uC81D\uD2B8\uC5D0 \uADF8 \uC218\uCE58\uB97C \uCD94\uAC00\uD55C \uB4A4 \uB2E4\uC2DC \uC800\uC7A5\uD558\uC138\uC694(\uADF8\uB300\uB85C \uC800\uC7A5\uD558\uB824\uBA74 force: true).`;
77085
- throw new Error(`\uC800\uC7A5 \uC804 \uC790\uB3D9 \uC810\uAC80\uC5D0\uC11C \uB9C9\uD614\uC2B5\uB2C8\uB2E4 \u2014 ${detail}. ${tail}`);
77228
+ throw new CareerMateError(`\uC800\uC7A5 \uC804 \uC790\uB3D9 \uC810\uAC80\uC5D0\uC11C \uB9C9\uD614\uC2B5\uB2C8\uB2E4 \u2014 ${detail}. ${tail}`, 400, "save_gate");
77086
77229
  }
77087
77230
  const { coverLetter, version: version2 } = coverLetterRepo.addVersion({
77088
77231
  cover_letter_id: input.cover_letter_id,
@@ -77109,7 +77252,7 @@ function saveCoverLetterVersion(input) {
77109
77252
  }
77110
77253
  function updateApplicationStatus(jobId, status, note) {
77111
77254
  const job = jobRepo.get(jobId);
77112
- if (!job) throw new Error(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${jobId}`);
77255
+ if (!job) throw notFound(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${jobId}`);
77113
77256
  const application = applicationRepo.setStatus(jobId, status, note);
77114
77257
  activityRepo.log(
77115
77258
  "application_status_changed",
@@ -77124,11 +77267,11 @@ function updateApplicationStatus(jobId, status, note) {
77124
77267
  }
77125
77268
  function saveInterviewPrep(input) {
77126
77269
  const job = jobRepo.get(input.job_id);
77127
- if (!job) throw new Error(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.job_id}`);
77270
+ if (!job) throw notFound(`\uACF5\uACE0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${input.job_id}`);
77128
77271
  const app = applicationRepo.getByJob(job.id);
77129
77272
  if (!app || !INTERVIEW_UNLOCK_STATUSES.includes(app.status)) {
77130
77273
  const current = app ? APPLICATION_STATUS_LABELS[app.status] : "\uBBF8\uC9C0\uC6D0";
77131
- throw new Error(
77274
+ throw conflict(
77132
77275
  `\uBA74\uC811 \uC900\uBE44\uB294 '\uC11C\uB958 \uD569\uACA9' \uC774\uC0C1 \uC0C1\uD0DC\uC5D0\uC11C\uB9CC \uC800\uC7A5\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uD604\uC7AC \uC0C1\uD0DC: '${current}'. \uBA3C\uC800 update_application_status\uB85C \uC0C1\uD0DC\uB97C \uC62C\uB9B0 \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.`
77133
77276
  );
77134
77277
  }
@@ -77209,7 +77352,7 @@ var WORKFLOWS = [
77209
77352
  '\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.)',
77210
77353
  "\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.",
77211
77354
  "\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).",
77212
- "\uACBD\uB825\xB7\uD504\uB85C\uC81D\uD2B8\xB7\uC2A4\uD0AC\uC774 \uD30C\uC545\uB418\uBA74 `add_experience`\xB7`add_project`\xB7`add_skill`\uB85C \uAC01\uAC01 \uC800\uC7A5\uD55C\uB2E4(\uC644\uC131\uB3C4\uC758 has_experience/has_skills\uC5D0 \uBC18\uC601).",
77355
+ "\uACBD\uB825\xB7\uD504\uB85C\uC81D\uD2B8\xB7\uC2A4\uD0AC\uC774 \uD30C\uC545\uB418\uBA74 `add_experience`\xB7`add_project`\xB7`add_skill`\uB85C \uC800\uC7A5\uD55C\uB2E4 \u2014 \uAC01 \uB3C4\uAD6C\uB294 \uBC30\uC5F4\uC744 \uBC1B\uC73C\uBBC0\uB85C \uD30C\uC545\uD55C \uD56D\uBAA9\uC744 \uD55C \uBC88\uC5D0 \uBAA8\uC544 \uB118\uAE34\uB2E4(\uD56D\uBAA9\uB9C8\uB2E4 \uBC18\uBCF5 \uD638\uCD9C\uD558\uC9C0 \uC54A\uB294\uB2E4). \uAC19\uC740 \uD56D\uBAA9\uC744 \uB2E4\uC2DC \uB123\uC5B4\uB3C4 \uC911\uBCF5 \uC5C6\uC774 \uAC31\uC2E0\uB41C\uB2E4(\uC644\uC131\uB3C4\uC758 has_experience/has_skills\uC5D0 \uBC18\uC601).",
77213
77356
  "\uAE30\uC874 \uC790\uAE30\uC18C\uAC1C\uC11C\uAC00 \uC788\uC73C\uBA74 `save_cover_letter_version`\uC73C\uB85C \uC800\uC7A5\uD55C\uB2E4(title+content\uBA74 v1\uB85C \uC800\uC7A5, \uC5C6\uC73C\uBA74 \uB098\uC911\uC5D0 \uC791\uC131 \uAC00\uB2A5\uD568\uC744 \uC548\uB0B4).",
77214
77357
  "`open_dashboard`\uB97C \uD638\uCD9C\uD574 \uC0AC\uC6A9\uC790\uAC00 \uC790\uAE30 \uB370\uC774\uD130\uB97C \uD655\uC778\uD558\uAC8C \uD55C\uB2E4.",
77215
77358
  "\uB2E4\uC2DC `get_onboarding_status`\uB85C \uC644\uC131\uB3C4\uB97C \uD655\uC778\uD558\uACE0, \uB2E4\uC74C \uB2E8\uACC4(\uACF5\uACE0 \uBD84\uC11D \uB4F1)\uB97C \uC81C\uC548\uD55C\uB2E4."
@@ -79018,11 +79161,12 @@ ${lines}`;
79018
79161
  {
79019
79162
  name: "add_experience",
79020
79163
  title: "\uACBD\uB825 \uCD94\uAC00",
79021
- description: "\uAD6C\uC870\uD654\uB41C \uACBD\uB825(\uC9C1\uC7A5 \uB2E8\uC704)\uC744 \uBCC4\uB3C4 \uD56D\uBAA9\uC73C\uB85C \uC800\uC7A5\uD569\uB2C8\uB2E4. \uC774\uB825\uC11C \uBCF8\uBB38(add_resume)\uACFC \uB2EC\uB9AC, \uD68C\uC0AC\xB7\uC9C1\uBB34\xB7\uAE30\uAC04\xB7\uC131\uACFC\uB97C \uD544\uB4DC\uB85C \uB098\uB220 \uC800\uC7A5\uD574 \uC801\uD569\uB3C4 \uBD84\uC11D/\uC790\uC18C\uC11C \uC791\uC131 \uC2DC \uC815\uBC00\uD558\uAC8C \uD65C\uC6A9\uB429\uB2C8\uB2E4. company\uB294 \uD544\uC218\uC774\uBA70, achievements\uC5D0\uB294 \uC815\uB7C9 \uC9C0\uD45C\uAC00 \uB2F4\uAE34 \uC131\uACFC\uB97C, tech\uC5D0\uB294 \uC0AC\uC6A9 \uAE30\uC220\uC744 \uBC30\uC5F4\uB85C \uB123\uC73C\uC138\uC694. \uC628\uBCF4\uB529 \uC644\uC131\uB3C4(get_onboarding_status)\uC758 has_experience\uB97C \uCC44\uC6B0\uB824\uBA74 \uC774 \uB3C4\uAD6C\uB85C 1\uAC74 \uC774\uC0C1 \uCD94\uAC00\uD574\uC57C \uD569\uB2C8\uB2E4.",
79022
- inputSchema: ExperienceInputSchema.shape,
79164
+ description: "\uAD6C\uC870\uD654\uB41C \uACBD\uB825(\uC9C1\uC7A5 \uB2E8\uC704)\uC744 \uC800\uC7A5\uD569\uB2C8\uB2E4. \uC774\uB825\uC11C\uC5D0\uC11C \uD30C\uC545\uD55C \uACBD\uB825\uC744 experiences \uBC30\uC5F4\uC5D0 \uD55C \uBC88\uC5D0 \uB2F4\uC544 \uD638\uCD9C\uD558\uC138\uC694(\uD55C \uAC74\uC774\uC5B4\uB3C4 \uBC30\uC5F4\uB85C \u2014 \uD56D\uBAA9\uB9C8\uB2E4 \uB3C4\uAD6C\uB97C \uBC18\uBCF5 \uD638\uCD9C\uD558\uC9C0 \uB9C8\uC138\uC694). \uC774\uB825\uC11C \uBCF8\uBB38(add_resume)\uACFC \uB2EC\uB9AC \uD68C\uC0AC\xB7\uC9C1\uBB34\xB7\uAE30\uAC04\xB7\uC131\uACFC\uB97C \uD544\uB4DC\uB85C \uB098\uB220 \uC800\uC7A5\uD574 \uC801\uD569\uB3C4 \uBD84\uC11D/\uC790\uC18C\uC11C \uC791\uC131 \uC2DC \uC815\uBC00\uD558\uAC8C \uD65C\uC6A9\uB429\uB2C8\uB2E4. \uAC01 \uD56D\uBAA9\uC740 company\uAC00 \uD544\uC218\uC774\uBA70, achievements\uC5D0\uB294 \uC815\uB7C9 \uC9C0\uD45C\uAC00 \uB2F4\uAE34 \uC131\uACFC\uB97C, tech\uC5D0\uB294 \uC0AC\uC6A9 \uAE30\uC220\uC744 \uBC30\uC5F4\uB85C \uB123\uC73C\uC138\uC694. \uAC19\uC740 \uD68C\uC0AC\xB7\uC9C1\uBB34\xB7\uC785\uC0AC\uC77C(start_date)\uC774 \uC77C\uCE58\uD558\uBA74 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC73C\uB2C8 \uC5EC\uB7EC \uBC88 \uC800\uC7A5\uD574\uB3C4 \uC548\uC804\uD569\uB2C8\uB2E4. \uC628\uBCF4\uB529 \uC644\uC131\uB3C4(get_onboarding_status)\uC758 has_experience\uB97C \uCC44\uC6B0\uB824\uBA74 1\uAC74 \uC774\uC0C1 \uCD94\uAC00\uD558\uC138\uC694.",
79165
+ inputSchema: ExperienceBatchInputSchema.shape,
79023
79166
  handler: (args) => {
79024
- const exp = addExperience(args);
79025
- return ok(`\uACBD\uB825 '${exp.company}${exp.role ? ` \xB7 ${exp.role}` : ""}'\uC744(\uB97C) \uCD94\uAC00\uD588\uC2B5\uB2C8\uB2E4.`, exp);
79167
+ const { records, created, updated } = addExperiences(args.experiences);
79168
+ const names = records.map((e) => `${e.company}${e.role ? ` \xB7 ${e.role}` : ""}`).join(", ");
79169
+ return ok(`\uACBD\uB825 ${records.length}\uAC74\uC744 \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4 (\uC2E0\uADDC ${created} \xB7 \uAC31\uC2E0 ${updated}): ${names}`, records);
79026
79170
  }
79027
79171
  },
79028
79172
  {
@@ -79040,11 +79184,12 @@ ${lines}`;
79040
79184
  {
79041
79185
  name: "add_project",
79042
79186
  title: "\uD504\uB85C\uC81D\uD2B8 \uCD94\uAC00",
79043
- description: "\uB300\uD45C \uD504\uB85C\uC81D\uD2B8\uB97C \uBCC4\uB3C4 \uD56D\uBAA9\uC73C\uB85C \uC800\uC7A5\uD569\uB2C8\uB2E4. name\uC740 \uD544\uC218\uC774\uBA70, highlights(\uD575\uC2EC \uC131\uACFC)\uC640 tech(\uC0AC\uC6A9 \uAE30\uC220)\uB97C \uBC30\uC5F4\uB85C \uB123\uC73C\uBA74 \uC790\uC18C\uC11C\xB7\uBA74\uC811 \uC900\uBE44\uC5D0\uC11C \uADFC\uAC70\uB85C \uD65C\uC6A9\uB429\uB2C8\uB2E4. url\uC5D0 \uACB0\uACFC\uBB3C \uB9C1\uD06C\uB97C \uB123\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
79044
- inputSchema: ProjectInputSchema.shape,
79187
+ description: "\uB300\uD45C \uD504\uB85C\uC81D\uD2B8\uB97C \uC800\uC7A5\uD569\uB2C8\uB2E4. projects \uBC30\uC5F4\uC5D0 \uC5EC\uB7EC \uAC1C\uB97C \uD55C \uBC88\uC5D0 \uB2F4\uC544 \uD638\uCD9C\uD558\uC138\uC694(\uD55C \uAC1C\uC5EC\uB3C4 \uBC30\uC5F4\uB85C \u2014 \uD56D\uBAA9\uB9C8\uB2E4 \uBC18\uBCF5 \uD638\uCD9C\uD558\uC9C0 \uB9C8\uC138\uC694). \uAC01 \uD56D\uBAA9\uC740 name\uC774 \uD544\uC218\uC774\uBA70, highlights(\uD575\uC2EC \uC131\uACFC)\uC640 tech(\uC0AC\uC6A9 \uAE30\uC220)\uB97C \uBC30\uC5F4\uB85C \uB123\uC73C\uBA74 \uC790\uC18C\uC11C\xB7\uBA74\uC811 \uC900\uBE44\uC5D0\uC11C \uADFC\uAC70\uB85C \uD65C\uC6A9\uB429\uB2C8\uB2E4. url\uC5D0 \uACB0\uACFC\uBB3C \uB9C1\uD06C\uB97C \uB123\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uAC19\uC740 \uC774\uB984\uC758 \uD504\uB85C\uC81D\uD2B8\uB294 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC73C\uB2C8 \uC5EC\uB7EC \uBC88 \uC800\uC7A5\uD574\uB3C4 \uC548\uC804\uD569\uB2C8\uB2E4.",
79188
+ inputSchema: ProjectBatchInputSchema.shape,
79045
79189
  handler: (args) => {
79046
- const prj = addProject(args);
79047
- return ok(`\uD504\uB85C\uC81D\uD2B8 '${prj.name}'\uC744(\uB97C) \uCD94\uAC00\uD588\uC2B5\uB2C8\uB2E4.`, prj);
79190
+ const { records, created, updated } = addProjects(args.projects);
79191
+ const names = records.map((p) => p.name).join(", ");
79192
+ return ok(`\uD504\uB85C\uC81D\uD2B8 ${records.length}\uAC74\uC744 \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4 (\uC2E0\uADDC ${created} \xB7 \uAC31\uC2E0 ${updated}): ${names}`, records);
79048
79193
  }
79049
79194
  },
79050
79195
  {
@@ -79062,11 +79207,12 @@ ${lines}`;
79062
79207
  {
79063
79208
  name: "add_skill",
79064
79209
  title: "\uAE30\uC220\uC2A4\uD0DD \uCD94\uAC00",
79065
- description: "\uBCF4\uC720 \uAE30\uC220\uC2A4\uD0DD\uC744 \uBCC4\uB3C4 \uD56D\uBAA9\uC73C\uB85C \uC800\uC7A5\uD569\uB2C8\uB2E4. name\uC740 \uD544\uC218\uC774\uBA70, category(\uC5B8\uC5B4/\uD504\uB808\uC784\uC6CC\uD06C/\uD234/\uC18C\uD504\uD2B8\uC2A4\uD0AC \uB4F1)\xB7level\xB7years\uB85C \uC219\uB828\uB3C4\uB97C \uD568\uAED8 \uAE30\uB85D\uD558\uBA74 \uACF5\uACE0 \uD0A4\uC6CC\uB4DC \uB9E4\uCE6D\uC774 \uC815\uD655\uD574\uC9D1\uB2C8\uB2E4. \uC628\uBCF4\uB529 \uC644\uC131\uB3C4\uC758 has_skills\uB97C \uCC44\uC6B0\uB824\uBA74 \uC774 \uB3C4\uAD6C\uB85C 1\uAC74 \uC774\uC0C1 \uCD94\uAC00\uD574\uC57C \uD569\uB2C8\uB2E4. \uC5EC\uB7EC \uAC1C\uBA74 \uB3C4\uAD6C\uB97C \uBC18\uBCF5 \uD638\uCD9C\uD558\uC138\uC694.",
79066
- inputSchema: SkillInputSchema.shape,
79210
+ description: "\uBCF4\uC720 \uAE30\uC220\uC2A4\uD0DD\uC744 \uC800\uC7A5\uD569\uB2C8\uB2E4. \uC5EC\uB7EC \uAC1C\uB97C skills \uBC30\uC5F4\uC5D0 \uD55C \uBC88\uC5D0 \uB2F4\uC544 \uD638\uCD9C\uD558\uC138\uC694(\uD55C \uAC1C\uC5EC\uB3C4 \uBC30\uC5F4\uB85C \u2014 \uAE30\uC220\uB9C8\uB2E4 \uB3C4\uAD6C\uB97C \uBC18\uBCF5 \uD638\uCD9C\uD558\uC9C0 \uB9C8\uC138\uC694). \uAC01 \uD56D\uBAA9\uC740 name\uC774 \uD544\uC218\uC774\uBA70, category(\uC5B8\uC5B4/\uD504\uB808\uC784\uC6CC\uD06C/\uD234/\uC18C\uD504\uD2B8\uC2A4\uD0AC \uB4F1)\xB7level\xB7years\uB85C \uC219\uB828\uB3C4\uB97C \uD568\uAED8 \uAE30\uB85D\uD558\uBA74 \uACF5\uACE0 \uD0A4\uC6CC\uB4DC \uB9E4\uCE6D\uC774 \uC815\uD655\uD574\uC9D1\uB2C8\uB2E4. \uAC19\uC740 \uC774\uB984\uC758 \uAE30\uC220\uC740 \uAC31\uC2E0\uB418\uACE0 \uC911\uBCF5 \uC0DD\uC131\uB418\uC9C0 \uC54A\uC73C\uB2C8 \uC5EC\uB7EC \uBC88 \uC800\uC7A5\uD574\uB3C4 \uC548\uC804\uD569\uB2C8\uB2E4. \uC628\uBCF4\uB529 \uC644\uC131\uB3C4\uC758 has_skills\uB97C \uCC44\uC6B0\uB824\uBA74 1\uAC1C \uC774\uC0C1 \uCD94\uAC00\uD558\uC138\uC694.",
79211
+ inputSchema: SkillBatchInputSchema.shape,
79067
79212
  handler: (args) => {
79068
- const skill = addSkill(args);
79069
- return ok(`\uAE30\uC220 '${skill.name}'\uC744(\uB97C) \uCD94\uAC00\uD588\uC2B5\uB2C8\uB2E4.`, skill);
79213
+ const { records, created, updated } = addSkills(args.skills);
79214
+ const names = records.map((s) => s.name).join(", ");
79215
+ return ok(`\uAE30\uC220 ${records.length}\uAC1C\uB97C \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4 (\uC2E0\uADDC ${created} \xB7 \uAC31\uC2E0 ${updated}): ${names}`, records);
79070
79216
  }
79071
79217
  },
79072
79218
  {