careermate 0.0.2 → 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/INSTALL.md +3 -3
- package/README.md +10 -8
- package/bin/careermate.mjs +2 -2
- package/dist/doctor.mjs +185 -39
- package/dist/mcp.mjs +188 -42
- package/dist/migrate.mjs +23 -1
- package/dist/public/lib.js +5 -1
- package/dist/public/pages/applications.js +7 -0
- package/dist/public/pages/jobs.js +22 -15
- package/dist/public/styles.css +9 -0
- package/dist/site/index.html +3 -43
- package/dist/site/llms-install.txt +4 -6
- package/dist/site/start.html +122 -102
- package/dist/web.mjs +138 -13
- package/docs/MCP_TOOLS.md +22 -7
- package/docs/START_WORKFLOW.md +2 -1
- package/docs/WORKFLOWS.md +1 -1
- package/package.json +2 -2
- package/packages/core/src/services.ts +67 -5
- package/packages/db/src/repositories.ts +0 -0
- package/packages/mcp-tools/src/tools.ts +21 -18
- package/packages/prompts/src/onboarding.ts +1 -1
- package/packages/shared/src/enums.ts +7 -2
- package/packages/shared/src/errors.ts +33 -0
- package/packages/shared/src/index.ts +1 -0
- package/packages/shared/src/schemas.ts +56 -2
- package/packages/workflows/src/definitions.ts +1 -1
- package/scripts/init.ts +11 -5
package/INSTALL.md
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
| 내가 누구인가 | MCP 등록 방식 |
|
|
28
28
|
|---|---|
|
|
29
29
|
| 사용자가 **Claude Desktop**을 쓰는 경우 | `careermate.zip` 압축 해제 후 폴더 추가 — **터미널 불필요** (`.mcpb` 직접 추가는 known issue) |
|
|
30
|
-
| **Claude Code** |
|
|
30
|
+
| **Claude Code** | 사용자가 여는 작업 폴더의 프로젝트 `.mcp.json`, 또는 `claude mcp add` (첫 실행 시 1회 승인) |
|
|
31
31
|
| **Codex** (OpenAI Codex CLI) | `~/.codex/config.toml`의 `[mcp_servers.careermate]` 테이블, 또는 `codex mcp add` |
|
|
32
32
|
|
|
33
33
|
대부분 `npm run init`(5단계)이 위 등록을 자동으로 처리합니다. 수동 등록 명령은 5단계 끝에 정리해 두었습니다.
|
|
@@ -105,7 +105,7 @@ npm run init
|
|
|
105
105
|
`init`이 하는 일:
|
|
106
106
|
- `~/.careermate` 폴더를 보장합니다.
|
|
107
107
|
- **감지된** AI 클라이언트를 자동 등록합니다(설정 파일 또는 그 상위 폴더가 있으면 감지된 것으로 간주).
|
|
108
|
-
- **Claude Code 프로젝트 `.mcp.json`은 항상**
|
|
108
|
+
- **Claude Code 프로젝트 `.mcp.json`은 항상** 이 명령을 실행한 작업 폴더에 기록합니다.
|
|
109
109
|
- 기존 설정은 변경 전에 타임스탬프 백업을 만들고, 사용자 env와 무관한 TOML 테이블은 보존합니다.
|
|
110
110
|
|
|
111
111
|
### 클라이언트별 결과
|
|
@@ -113,7 +113,7 @@ npm run init
|
|
|
113
113
|
| 클라이언트 | 등록 결과 |
|
|
114
114
|
|---|---|
|
|
115
115
|
| **Claude Desktop** | `careermate.zip` 압축 해제 후 폴더 추가(아래) — `init`과 별개. `.mcpb` 직접 추가는 known issue |
|
|
116
|
-
| **Claude Code** |
|
|
116
|
+
| **Claude Code** | 현재 작업 폴더에 프로젝트 `.mcp.json` 기록 → 첫 실행 시 **1회 승인** 필요 |
|
|
117
117
|
| **Codex** | `~/.codex/config.toml`의 `[mcp_servers.careermate]` 기록 → Codex 안에서 `/mcp`로 확인 |
|
|
118
118
|
|
|
119
119
|
`.mcp.json`은 최상위 `"mcpServers"` 아래에 서버별 `{"type":"stdio","command":...,"args":[...]}` 형태로 들어갑니다.
|
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
## 주요 기능
|
|
32
32
|
|
|
33
|
-
- **AI와 대화로 모든 작업** — MCP 도구
|
|
33
|
+
- **AI와 대화로 모든 작업** — MCP 도구 35개로 온보딩·프로필·이력서·자소서·공고·핏 분석·지원 상태·면접 준비·전문가 플레이북·AI 티 안 나는 글쓰기까지 처리.
|
|
34
34
|
- **지원 상태 8단계 관리** — `draft`(작성 중) · `planned`(지원 예정) · `applied`(지원 완료) · `document_passed`(서류 합격) · `interview`(면접 진행) · `final_passed`(최종 합격) · `rejected`(불합격) · `on_hold`(보류). `document_passed` 이상에서 면접 준비가 해금됩니다.
|
|
35
35
|
- **자기소개서 버전 관리** — 공고별로 자소서 버전을 쌓고 타임라인으로 비교, 파일로 내보내기.
|
|
36
36
|
- **채용공고 파싱** — 붙여넣은 공고 텍스트를 구조화해 저장.
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
| 프로세스 | 실행 | 역할 |
|
|
47
47
|
| --- | --- | --- |
|
|
48
48
|
| **대시보드 웹 서버** | `npm start` | `http://127.0.0.1:4319` — 내 데이터를 눈으로 확인·관리. `127.0.0.1`에만 바인딩. |
|
|
49
|
-
| **MCP 서버** | `npm run mcp` | stdio 기반. 보통 AI 클라이언트가 자동 실행. 도구
|
|
49
|
+
| **MCP 서버** | `npm run mcp` | stdio 기반. 보통 AI 클라이언트가 자동 실행. 도구 35개 제공. |
|
|
50
50
|
|
|
51
51
|
---
|
|
52
52
|
|
|
@@ -141,7 +141,7 @@ npx -y careermate init
|
|
|
141
141
|
|
|
142
142
|
---
|
|
143
143
|
|
|
144
|
-
## MCP 도구 한눈에 (
|
|
144
|
+
## MCP 도구 한눈에 (35개)
|
|
145
145
|
|
|
146
146
|
| 분류 | 도구 |
|
|
147
147
|
| --- | --- |
|
|
@@ -157,6 +157,7 @@ npx -y careermate init
|
|
|
157
157
|
| 면접 준비 | `save_interview_prep` |
|
|
158
158
|
| 글쓰기 | `get_writing_style_guide` (AI 티 안 나는 한국어 글쓰기 규칙) |
|
|
159
159
|
| 전문가 지식 (Career-OS) | `get_playbook` (도메인 플레이북 16종) · `get_verifier` (저장 전 검증 루브릭 6종) |
|
|
160
|
+
| 저장 전 점검 | `validate_cover_letter` (자소서 저장 전 미리보기) · `set_verify_mode` (점검 엄격도 기본/엄격) |
|
|
160
161
|
| 대시보드/활동 | `open_dashboard` · `open_application` · `list_recent_activity` · `get_workflow_guide` |
|
|
161
162
|
| 업데이트 | `check_for_update` · `update_careermate` |
|
|
162
163
|
|
|
@@ -175,7 +176,7 @@ CareerMate/
|
|
|
175
176
|
│ ├─ shared/ # 공용 타입·zod 스키마·유틸
|
|
176
177
|
│ ├─ db/ # node:sqlite DB 접근·스키마·마이그레이션
|
|
177
178
|
│ ├─ core/ # 도메인 유스케이스
|
|
178
|
-
│ ├─ mcp-tools/ # MCP 도구
|
|
179
|
+
│ ├─ mcp-tools/ # MCP 도구 35개 정의
|
|
179
180
|
│ ├─ knowledge/ # Career-OS 전문가 플레이북·검증 루브릭 serve
|
|
180
181
|
│ ├─ exporters/ # 내보내기(자소서 등)
|
|
181
182
|
│ ├─ parsers/ # 채용공고 파싱
|
|
@@ -301,7 +302,7 @@ You can open the dashboard at any time to see your stored data. For a step-by-st
|
|
|
301
302
|
|
|
302
303
|
## Key features
|
|
303
304
|
|
|
304
|
-
- **Everything through conversation with AI** —
|
|
305
|
+
- **Everything through conversation with AI** — 35 MCP tools cover onboarding, profile, resumes, cover letters, job postings, fit analysis, application status, interview prep, expert playbooks, and human-sounding writing.
|
|
305
306
|
- **8-stage application status** — `draft` · `planned` · `applied` · `document_passed` · `interview` · `final_passed` · `rejected` · `on_hold`. Interview prep unlocks at `document_passed` or later.
|
|
306
307
|
- **Cover-letter versioning** — Stack versions per posting, compare on a timeline, export to a file.
|
|
307
308
|
- **Job-posting parsing** — Turn pasted posting text into structured records.
|
|
@@ -317,7 +318,7 @@ Two local processes share the same database.
|
|
|
317
318
|
| Process | Run | Role |
|
|
318
319
|
| --- | --- | --- |
|
|
319
320
|
| **Dashboard web server** | `npm start` | `http://127.0.0.1:4319` — view and manage your data. Binds to `127.0.0.1` only. |
|
|
320
|
-
| **MCP server** | `npm run mcp` | stdio-based. Usually launched automatically by the AI client. Provides
|
|
321
|
+
| **MCP server** | `npm run mcp` | stdio-based. Usually launched automatically by the AI client. Provides 35 tools. |
|
|
321
322
|
|
|
322
323
|
---
|
|
323
324
|
|
|
@@ -412,7 +413,7 @@ Dark mode is supported.
|
|
|
412
413
|
|
|
413
414
|
---
|
|
414
415
|
|
|
415
|
-
## MCP tools at a glance (
|
|
416
|
+
## MCP tools at a glance (35)
|
|
416
417
|
|
|
417
418
|
| Category | Tools |
|
|
418
419
|
| --- | --- |
|
|
@@ -428,6 +429,7 @@ Dark mode is supported.
|
|
|
428
429
|
| Interview prep | `save_interview_prep` |
|
|
429
430
|
| Writing | `get_writing_style_guide` (rules for human-sounding Korean writing) |
|
|
430
431
|
| Expert knowledge (Career-OS) | `get_playbook` (16 domain playbooks) · `get_verifier` (6 pre-save rubrics) |
|
|
432
|
+
| Pre-save check | `validate_cover_letter` (dry-run preview before saving) · `set_verify_mode` (check strictness: default/strict) |
|
|
431
433
|
| Dashboard / activity | `open_dashboard` · `open_application` · `list_recent_activity` · `get_workflow_guide` |
|
|
432
434
|
| Update | `check_for_update` · `update_careermate` |
|
|
433
435
|
|
|
@@ -446,7 +448,7 @@ CareerMate/
|
|
|
446
448
|
│ ├─ shared/ # Shared types, zod schemas, utils
|
|
447
449
|
│ ├─ db/ # node:sqlite DB access, schema, migrations
|
|
448
450
|
│ ├─ core/ # Domain use cases
|
|
449
|
-
│ ├─ mcp-tools/ # The
|
|
451
|
+
│ ├─ mcp-tools/ # The 35 MCP tool definitions
|
|
450
452
|
│ ├─ knowledge/ # Career-OS expert playbooks & verifier rubrics (serve)
|
|
451
453
|
│ ├─ exporters/ # Exporters (cover letters, etc.)
|
|
452
454
|
│ ├─ parsers/ # Job-posting parsing
|
package/bin/careermate.mjs
CHANGED
|
@@ -46,12 +46,12 @@ if (isHelp || !ENTRIES[cmd]) {
|
|
|
46
46
|
'',
|
|
47
47
|
'사용법: careermate <명령>',
|
|
48
48
|
'',
|
|
49
|
-
' init AI 클라이언트에 CareerMate를 자동 연결(MCP 설정
|
|
49
|
+
' init AI 클라이언트에 CareerMate를 자동 연결(MCP 설정 등록, Claude Code는 현재 폴더 기준)',
|
|
50
50
|
' mcp MCP 서버 실행 (보통 AI 클라이언트가 자동 호출)',
|
|
51
51
|
' start 로컬 대시보드 실행 (http://127.0.0.1:4319)',
|
|
52
52
|
' doctor 설치/환경 점검',
|
|
53
53
|
'',
|
|
54
|
-
'처음이라면: careermate init → AI 클라이언트 재시작',
|
|
54
|
+
'처음이라면: 사용할 작업 폴더에서 careermate init → AI 클라이언트 재시작',
|
|
55
55
|
];
|
|
56
56
|
// 명시적 도움말은 stdout+exit0, 알 수 없는 명령은 stderr+exit1
|
|
57
57
|
// (stdout은 MCP 전송 채널이므로 오류 안내로 오염시키지 않는다).
|
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
|
|
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
|
-
|
|
68189
|
+
norm2 += w;
|
|
68190
68190
|
}
|
|
68191
68191
|
}
|
|
68192
|
-
const pix = out[ij] = Math.round(sum /
|
|
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
|
-
|
|
71505
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
|
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: [...
|
|
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
|
|
77048
|
-
const
|
|
77049
|
-
|
|
77050
|
-
return
|
|
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
|
|
77053
|
-
const
|
|
77054
|
-
activityRepo.log(
|
|
77055
|
-
|
|
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
|
|
77058
|
-
const
|
|
77059
|
-
activityRepo.log(
|
|
77060
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 \
|
|
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 \
|
|
79022
|
-
inputSchema:
|
|
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
|
|
79025
|
-
|
|
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 \
|
|
79044
|
-
inputSchema:
|
|
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
|
|
79047
|
-
|
|
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 \
|
|
79066
|
-
inputSchema:
|
|
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
|
|
79069
|
-
|
|
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
|
{
|