careermate 0.1.0
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/README.md +256 -0
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/apps/mcp/src/index.ts +66 -0
- package/apps/web/DESIGN_GUIDE.md +105 -0
- package/apps/web/UI_CONTRACT.md +44 -0
- package/apps/web/public/app.js +118 -0
- package/apps/web/public/fonts/PretendardVariable.woff2 +0 -0
- package/apps/web/public/index.html +41 -0
- package/apps/web/public/lib.js +282 -0
- package/apps/web/public/pages/applications.js +98 -0
- package/apps/web/public/pages/documents.js +446 -0
- package/apps/web/public/pages/home.js +263 -0
- package/apps/web/public/pages/interview.js +230 -0
- package/apps/web/public/pages/jobs.js +494 -0
- package/apps/web/public/pages/profile.js +576 -0
- package/apps/web/public/pages/settings.js +233 -0
- package/apps/web/public/styles.css +426 -0
- package/apps/web/src/exports.ts +68 -0
- package/apps/web/src/http.ts +180 -0
- package/apps/web/src/index.ts +49 -0
- package/apps/web/src/info.ts +50 -0
- package/apps/web/src/routes.ts +350 -0
- package/apps/web/src/security.ts +102 -0
- package/apps/web/src/server.ts +141 -0
- package/apps/web/src/settings.ts +88 -0
- package/bin/careermate.mjs +74 -0
- package/dist/careermate.mcpb +0 -0
- package/dist/install-page/index.html +474 -0
- package/dist/install-page/style.css +391 -0
- package/dist/install-page/vercel.json +20 -0
- package/dist/mcp-smoke.err +3 -0
- package/dist/mcp.mjs +23704 -0
- package/dist/mcpb-stage/README.md +219 -0
- package/dist/mcpb-stage/dist/install-page/index.html +434 -0
- package/dist/mcpb-stage/dist/install-page/style.css +407 -0
- package/dist/mcpb-stage/dist/install-page/vercel.json +20 -0
- package/dist/mcpb-stage/dist/mcp.mjs +23704 -0
- package/dist/mcpb-stage/dist/public/app.js +118 -0
- package/dist/mcpb-stage/dist/public/fonts/PretendardVariable.woff2 +0 -0
- package/dist/mcpb-stage/dist/public/index.html +41 -0
- package/dist/mcpb-stage/dist/public/lib.js +282 -0
- package/dist/mcpb-stage/dist/public/pages/applications.js +98 -0
- package/dist/mcpb-stage/dist/public/pages/documents.js +446 -0
- package/dist/mcpb-stage/dist/public/pages/home.js +263 -0
- package/dist/mcpb-stage/dist/public/pages/interview.js +230 -0
- package/dist/mcpb-stage/dist/public/pages/jobs.js +494 -0
- package/dist/mcpb-stage/dist/public/pages/profile.js +576 -0
- package/dist/mcpb-stage/dist/public/pages/settings.js +233 -0
- package/dist/mcpb-stage/dist/public/styles.css +420 -0
- package/dist/mcpb-stage/dist/web.mjs +7240 -0
- package/dist/mcpb-stage/manifest.json +40 -0
- package/dist/public/app.js +118 -0
- package/dist/public/fonts/PretendardVariable.woff2 +0 -0
- package/dist/public/index.html +41 -0
- package/dist/public/lib.js +282 -0
- package/dist/public/pages/applications.js +98 -0
- package/dist/public/pages/documents.js +446 -0
- package/dist/public/pages/home.js +263 -0
- package/dist/public/pages/interview.js +230 -0
- package/dist/public/pages/jobs.js +494 -0
- package/dist/public/pages/profile.js +576 -0
- package/dist/public/pages/settings.js +233 -0
- package/dist/public/styles.css +426 -0
- package/dist/web.mjs +7240 -0
- package/docs/ARCHITECTURE.md +208 -0
- package/docs/CHANGES_V1.md +103 -0
- package/docs/DATA_MODEL.md +460 -0
- package/docs/DECISIONS.md +277 -0
- package/docs/DEMO.md +242 -0
- package/docs/INSTALL.md +148 -0
- package/docs/INSTALL_AND_USAGE.md +99 -0
- package/docs/MCP_TOOLS.md +233 -0
- package/docs/ROADMAP.md +134 -0
- package/docs/START_WORKFLOW.md +125 -0
- package/docs/SUPPORTED_AI_APPS.md +60 -0
- package/docs/TODO.md +57 -0
- package/docs/UX_NOTES.md +247 -0
- package/docs/WORKFLOWS.md +200 -0
- package/install-page/index.html +474 -0
- package/install-page/style.css +391 -0
- package/install-page/vercel.json +20 -0
- package/package.json +68 -0
- package/packages/core/src/context.ts +74 -0
- package/packages/core/src/index.ts +8 -0
- package/packages/core/src/onboarding.ts +81 -0
- package/packages/core/src/services.ts +146 -0
- package/packages/core/src/summary.ts +104 -0
- package/packages/db/src/connection.ts +46 -0
- package/packages/db/src/index.ts +22 -0
- package/packages/db/src/paths.ts +41 -0
- package/packages/db/src/repositories.ts +828 -0
- package/packages/db/src/runtime.ts +58 -0
- package/packages/db/src/schema.ts +189 -0
- package/packages/exporters/src/html.ts +113 -0
- package/packages/exporters/src/index.ts +364 -0
- package/packages/exporters/src/markdown.ts +178 -0
- package/packages/mcp-tools/src/bridge.ts +83 -0
- package/packages/mcp-tools/src/index.ts +8 -0
- package/packages/mcp-tools/src/result.ts +49 -0
- package/packages/mcp-tools/src/tools.ts +455 -0
- package/packages/parsers/src/html.ts +86 -0
- package/packages/parsers/src/index.ts +228 -0
- package/packages/parsers/src/keywords.ts +151 -0
- package/packages/prompts/src/humanize.ts +59 -0
- package/packages/prompts/src/index.ts +82 -0
- package/packages/prompts/src/install.ts +43 -0
- package/packages/prompts/src/onboarding.ts +35 -0
- package/packages/prompts/src/system.ts +53 -0
- package/packages/shared/src/enums.ts +103 -0
- package/packages/shared/src/index.ts +18 -0
- package/packages/shared/src/schemas.ts +398 -0
- package/packages/workflows/src/definitions.ts +107 -0
- package/packages/workflows/src/index.ts +39 -0
- package/scripts/build-dist.mjs +62 -0
- package/scripts/build-mcpb.mjs +70 -0
- package/scripts/doctor.ts +81 -0
- package/scripts/init.ts +342 -0
- package/scripts/mcp-probe.ts +55 -0
- package/scripts/migrate.ts +6 -0
- package/scripts/run.mjs +33 -0
- package/scripts/seed.ts +129 -0
- package/scripts/test.ts +117 -0
- package/scripts/ui-smoke.ts +73 -0
- package/tsconfig.json +29 -0
package/docs/TODO.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# CareerMate TODO (향후 방향)
|
|
2
|
+
|
|
3
|
+
아래 항목은 **v1 범위에서 명시적으로 제외**한다. 다만 패키지 경계·데이터 모델·도구 인터페이스는 이후 추가를 수용하도록 **구조를 열어둔다**.
|
|
4
|
+
|
|
5
|
+
> 사이트별 크롤러, 클라우드 동기화, DOCX export, i18n 같은 **기능/패키지 단위 백로그**는 [ROADMAP.md](./ROADMAP.md)가 관리한다(구조 개방 근거 포함). 이 문서는 **제품 방향(v1.5 / v2)과 미해결 과제**만 다루며, 중복 기재하지 않는다.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## v1.5
|
|
10
|
+
|
|
11
|
+
설치형 데스크톱 앱으로의 전환.
|
|
12
|
+
|
|
13
|
+
- **Electron** 셸 — 대시보드를 데스크톱 앱으로 래핑(정적 자산이라 셸로 감싸기 적합).
|
|
14
|
+
- **Windows / macOS 설치형 앱** — 더블클릭 설치, Node 동봉.
|
|
15
|
+
- **자동 업데이트** — 백그라운드 갱신.
|
|
16
|
+
- **트레이 상주** — MCP·대시보드 프로세스를 트레이에서 관리(시작/중지/상태).
|
|
17
|
+
- **`CareerMate.exe`** — 단일 실행 진입점.
|
|
18
|
+
|
|
19
|
+
## v2
|
|
20
|
+
|
|
21
|
+
로컬 stdio의 한계를 넘어 원격·범용 AI 클라이언트까지 확장.
|
|
22
|
+
|
|
23
|
+
- **Gateway Mode** — 로컬 stdio를 원격에서 접근 가능한 형태로 중계.
|
|
24
|
+
- **ChatGPT 일반 채팅** — 웹/모바일 ChatGPT에서 사용(현재는 로컬 stdio라 불가).
|
|
25
|
+
- **Claude 일반 채팅** — Claude 웹 앱 연동.
|
|
26
|
+
- **Gemini** — Gemini 클라이언트 연동.
|
|
27
|
+
- **oneTimeToken** — Gateway 접근을 위한 일회용 토큰 핸드셰이크.
|
|
28
|
+
- **WebSocket Local Agent** — 로컬 에이전트와 원격 클라이언트 간 양방향 채널.
|
|
29
|
+
- **MCP Gateway** — 원격 URL 기반 MCP 엔드포인트 노출.
|
|
30
|
+
|
|
31
|
+
## 미래 방향
|
|
32
|
+
|
|
33
|
+
향후 `CareerMate.exe`가 중심에 설 수 있고(현재는 v1.5 과제, v1 비범위), 그 구도에서 **AI는 "연결 어댑터"** 역할을 한다. ChatGPT / Claude / Gemini / Codex / Claude Code 등은 두뇌를 제공하는 별개 인터페이스일 뿐, 커리어 데이터의 단일 출처(source of truth)는 항상 로컬의 CareerMate다. 사용자는 그날 손에 잡히는 AI를 골라 같은 서랍장에 연결한다. 즉 AI는 **갈아끼우는 부품**, CareerMate는 **고정된 본체**라는 구도다.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## v1에서 제외 (구조는 열어둠)
|
|
38
|
+
|
|
39
|
+
다음은 v1에서 구현하지 않지만 설계상 길을 막지 않는다.
|
|
40
|
+
|
|
41
|
+
- Electron
|
|
42
|
+
- exe
|
|
43
|
+
- Gateway
|
|
44
|
+
- WebSocket
|
|
45
|
+
- oneTimeToken
|
|
46
|
+
- Cloud 저장 / 클라우드 동기화
|
|
47
|
+
- 인증 서버
|
|
48
|
+
|
|
49
|
+
## 열린 TODO / 미해결
|
|
50
|
+
|
|
51
|
+
확정·보완이 필요한 알려진 공백. 잊지 않도록 기록한다.
|
|
52
|
+
|
|
53
|
+
- **npm 게시 완료** — `careermate@0.1.0` 공개 게시됨. `npx -y careermate init`가 **동작한다**. (소스 기반 `npm install` → `npm run init`도 그대로 유효.)
|
|
54
|
+
- **.mcpb GitHub Release 생성됨(접근 제한)** — Release `v0.1.0`에 `careermate.mcpb` 첨부. 단 저장소가 **비공개**라 공개 다운로드 링크는 아직 없음. → 저장소를 public으로 전환하면 공개 다운로드 활성화.
|
|
55
|
+
- **공개 repo URL 확정** — git remote 기준 정본은 `osntak/CareerMate`(현재 **비공개**). 공개 전환 여부는 별도 결정 사항.
|
|
56
|
+
- **THIRD_PARTY_NOTICES — `im-not-ai`(MIT)** 항목의 저작권자/연도가 비어 있다. 실제 저작권자·연도로 채우기.
|
|
57
|
+
- **THIRD_PARTY_NOTICES — `im-not-ai`(MIT)** 항목의 저작권자/연도가 비어 있다. 실제 저작권자·연도로 채우기.
|
package/docs/UX_NOTES.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# UI/UX 설계 노트
|
|
2
|
+
|
|
3
|
+
CareerMate 대시보드의 화면 설계 의도를 정리한 문서다. 무엇을 어떻게 만들었는지뿐 아니라 **왜 그렇게 했는지**를 남겨, 새 화면을 추가하거나 기존 화면을 손볼 때 일관성을 잃지 않도록 한다.
|
|
4
|
+
|
|
5
|
+
대시보드는 프레임워크·번들러·CDN 없이 동작하는 바닐라 JS + 자체 CSS 디자인 시스템으로 만들어졌다. 모든 컴포넌트와 토큰은 다음 두 파일에 모여 있다.
|
|
6
|
+
|
|
7
|
+
- `apps/web/public/lib.js` — DOM 헬퍼, API 클라이언트, 공용 컴포넌트
|
|
8
|
+
- `apps/web/public/styles.css` — 디자인 토큰과 컴포넌트 스타일
|
|
9
|
+
|
|
10
|
+
화면을 만드는 규칙은 [`apps/web/UI_CONTRACT.md`](../apps/web/UI_CONTRACT.md)에, 기준이 되는 참고 구현은 [`apps/web/public/pages/home.js`](../apps/web/public/pages/home.js)에 있다. 이 문서는 그 둘의 배경이 되는 설계 원칙을 담는다.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 1. 디자인 원칙
|
|
15
|
+
|
|
16
|
+
### 1.1 "진짜 생산성 도구"를 지향한다
|
|
17
|
+
|
|
18
|
+
`styles.css` 첫머리 주석이 톤을 규정한다.
|
|
19
|
+
|
|
20
|
+
> CareerMate design system — a calm, dense, real-productivity-tool aesthetic.
|
|
21
|
+
> Inspired by the restraint of Linear / Notion / Raycast — not AI-flashy.
|
|
22
|
+
|
|
23
|
+
차분하고(calm), 밀도 있고(dense), 실제 일이 되는(real-productivity) 도구가 목표다. 화려한 그라데이션이나 과장된 모션이 아니라, 매일 들여다봐도 피로하지 않은 절제된 화면을 만든다.
|
|
24
|
+
|
|
25
|
+
### 1.2 AI 슬롭(AI slop)을 회피한다
|
|
26
|
+
|
|
27
|
+
CareerMate는 사용자의 AI(ChatGPT·Claude·Gemini)가 MCP로 호출하는 도구이지, 내부에 LLM이 없다. 그래서 화면이 "AI 제품"처럼 보일 이유가 없다. [`UI_CONTRACT.md`](../apps/web/UI_CONTRACT.md)의 컨벤션이 이를 못박는다.
|
|
28
|
+
|
|
29
|
+
> Dense, calm, real-product feel. Avoid emoji-heavy or flashy "AI" styling.
|
|
30
|
+
|
|
31
|
+
- 반짝이·로봇·마법봉 같은 과시적 표현을 화면 곳곳에 뿌리지 않는다. (`sparkle` 아이콘이 `lib.js`에 존재하지만 강조 포인트로 아껴 쓴다.)
|
|
32
|
+
- 이모지를 정보 전달 수단으로 남용하지 않는다.
|
|
33
|
+
- 결과를 "AI가 만들었다"는 식으로 포장하지 않는다. 자기소개서 버전 출처는 `직접 입력 / 파일 업로드 / AI 생성 / 직접 수정`(`documents.js`의 `SOURCE_LABELS`)처럼 담백한 라벨로 사실만 표기한다.
|
|
34
|
+
|
|
35
|
+
### 1.3 밀도(density)
|
|
36
|
+
|
|
37
|
+
한 화면에서 많은 정보를 빠르게 훑을 수 있어야 한다.
|
|
38
|
+
|
|
39
|
+
- 기본 본문 폰트 14px, 줄간격 1.55 (`body`).
|
|
40
|
+
- 컴포넌트마다 의도된 작은 글자 크기 단계가 있다. 예: 네비 라벨 11px, 카드 부제 12.5px, 통계 값 26px.
|
|
41
|
+
- `.stat`, `.table`, `.board-card`, `.timeline` 등으로 목록·표·칸반·타임라인을 조밀하게 배치한다.
|
|
42
|
+
- 숫자는 `font-variant-numeric: tabular-nums`(`.tnum`)로 자릿수를 고정해 표/통계에서 흔들리지 않게 한다.
|
|
43
|
+
|
|
44
|
+
밀도는 "빽빽함"이 아니라 "여백을 낭비하지 않음"이다. 카드·셀의 패딩은 일정한 토큰 체계를 따른다.
|
|
45
|
+
|
|
46
|
+
### 1.4 친절한 empty state
|
|
47
|
+
|
|
48
|
+
비어 있는 화면이야말로 첫인상이다. 모든 목록은 비었을 때 다음을 제공한다(컨벤션상 필수, [`UI_CONTRACT.md`](../apps/web/UI_CONTRACT.md) "Always provide a friendly empty state").
|
|
49
|
+
|
|
50
|
+
1. 무엇을 하는 공간인지 한 문장 설명
|
|
51
|
+
2. **MCP로 자동 저장된다**는 안내 — 사용자가 AI와 대화하면 채워진다는 점을 알려준다
|
|
52
|
+
3. 직접 추가할 수 있는 1차 액션 버튼
|
|
53
|
+
|
|
54
|
+
예: 자기소개서 탭(`documents.js`)의 빈 상태.
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
title: '자기소개서가 없어요'
|
|
58
|
+
body: 'ChatGPT·Claude에게 작성을 요청하면 자동으로 여기에 저장되고,
|
|
59
|
+
수정할 때마다 버전이 기록됩니다. 직접 추가할 수도 있어요.'
|
|
60
|
+
action: '새 자기소개서'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
공고 목록(`home.js`의 `RecentJobs`), 문서 탭, 면접 준비 영역도 같은 패턴을 따른다. 빈 상태는 `EmptyState({ iconName, title, body, action })` 컴포넌트(`lib.js`)로만 만든다.
|
|
64
|
+
|
|
65
|
+
### 1.5 비개발자 용어
|
|
66
|
+
|
|
67
|
+
사용자는 개발자가 아닐 수 있다. 화면 문구는 한국어 우선이고, 기술 용어를 노출하지 않는다.
|
|
68
|
+
|
|
69
|
+
- 상태 코드는 내부적으로 `document_passed`지만, 사용자에게는 항상 라벨(`서류 합격`)로 보인다. 8단계 라벨은 [`UI_CONTRACT.md`](../apps/web/UI_CONTRACT.md)에 정의돼 있다: `작성 중 / 지원 예정 / 지원 완료 / 서류 합격 / 면접 진행 / 최종 합격 / 불합격 / 보류`.
|
|
70
|
+
- 오류 메시지도 한국어다. `api()`(`lib.js`)는 실패 시 `요청 실패 (${status})`로, `toastError`는 제목 `오류`로 표시한다.
|
|
71
|
+
- 상대 시간도 사람 말투다: `방금 전 / N분 전 / N시간 전 / N일 전`(`fmtRelative`).
|
|
72
|
+
|
|
73
|
+
### 1.6 MCP·localhost는 설정에서만
|
|
74
|
+
|
|
75
|
+
CareerMate의 동작 기반(127.0.0.1 바인딩, MCP stdio 서버, 데이터 디렉터리, server.json 핸드셰이크)은 사용자가 매일 신경 쓸 대상이 아니다. 그래서 이런 기술적 디테일은 **Settings 페이지에만** 둔다. Home·Jobs·Applications 같은 일상 화면에는 포트 번호나 MCP 연결 상태 같은 내부 개념을 노출하지 않는다. 일상 화면은 "내 커리어"라는 도메인 언어로만 이야기한다.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 2. 정보 구조 (7페이지)
|
|
80
|
+
|
|
81
|
+
좌측 고정 사이드바(`--sidebar-w: 248px`)로 7개 페이지를 오간다. 라우팅은 해시 기반(`navigate(hash)` → `location.hash`)이고, `params`는 페이지 id 뒤 경로 세그먼트다(`#/jobs/abc` → `params=['abc']`).
|
|
82
|
+
|
|
83
|
+
| 페이지 | 역할 |
|
|
84
|
+
| --- | --- |
|
|
85
|
+
| **Home** | "지금 뭘 해야 하지?"를 한눈에. 통계 타일, 온보딩 카드, 최근 공고, 진행 중 지원, 면접 할 일, 최근 활동 |
|
|
86
|
+
| **Profile** | 프로필·경험·프로젝트·스킬 관리, 프로필 완성도 |
|
|
87
|
+
| **Jobs** | 저장된 공고 목록과 상세(적합도, 분석, 연결된 지원) |
|
|
88
|
+
| **Applications** | 지원 현황 칸반(8단계 상태 보드) |
|
|
89
|
+
| **Documents** | 자기소개서(버전 타임라인)와 이력서·경력기술서 문서 |
|
|
90
|
+
| **Interview** | 면접 준비 — 서류 합격 이상에서 해금 |
|
|
91
|
+
| **Settings** | 데이터 위치, MCP 연결, 테마 등 기술적 설정 |
|
|
92
|
+
|
|
93
|
+
### Home이 정보 구조의 중심인 이유
|
|
94
|
+
|
|
95
|
+
Home(`home.js`)은 다른 페이지로의 진입점이다. 구성:
|
|
96
|
+
|
|
97
|
+
- **PageHead** — 프로필 이름이 있으면 `{name}님, 안녕하세요`, 없으면 `환영합니다`. 부제는 `ChatGPT·Claude와 함께 쓰는 내 커리어 흐름 관리 도구`.
|
|
98
|
+
- **온보딩 카드** — 온보딩 미완료이거나 다음 할 일이 남았을 때만 노출(`!s.onboarding.completed || s.onboarding.next_steps.length`). 진행 바로 프로필 완성도를 보여주고, 다음 할 일을 최대 4개까지 나열한다.
|
|
99
|
+
- **통계 타일 4개**(`grid--4`) — 저장된 공고 / 진행 중 지원 / 자기소개서 / 면접 준비 필요. 면접 준비가 있으면 힌트 `서류 통과 후 준비하세요`.
|
|
100
|
+
- **2열 레이아웃**(`grid--2`) — 좌: 최근 공고 + 진행 중 지원, 우: 면접 할 일 + 최근 활동.
|
|
101
|
+
|
|
102
|
+
핵심은 **"무엇을 보여줄지보다, 무엇을 다음에 할지"**를 안내한다는 점이다.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 3. 디자인 시스템
|
|
107
|
+
|
|
108
|
+
### 3.1 토큰 (CSS 변수)
|
|
109
|
+
|
|
110
|
+
`styles.css`의 `:root`에 모든 토큰이 모여 있다. 화면에서 색·반경·간격을 하드코딩하지 않고 토큰만 쓴다.
|
|
111
|
+
|
|
112
|
+
- **중립 팔레트**: `--bg`, `--bg-subtle`, `--bg-sunken`, `--surface`, `--surface-hover`, `--border`, `--border-strong`, `--text`, `--text-secondary`, `--text-tertiary`.
|
|
113
|
+
- **악센트**: 차분한 인디고. `--accent: #4f46e5`, `--accent-hover`, `--accent-soft`, `--accent-border`.
|
|
114
|
+
- **시맨틱 색**: `green / teal / blue / violet / amber / red / slate` 각각 본색·`-soft`·`-border` 3종. 상태 배지와 점수 색(`score-strong/mid/weak`)에 쓴다.
|
|
115
|
+
- **엘리베이션**: `--shadow-sm/md/lg`.
|
|
116
|
+
- **지오메트리**: `--radius-sm: 6px`, `--radius: 9px`, `--radius-lg: 14px`, `--sidebar-w`, `--topbar-h: 56px`, `--maxw: 1080px`.
|
|
117
|
+
- **타이포**: `--font`(Pretendard 우선, 한글 폴백 포함), `--mono`.
|
|
118
|
+
|
|
119
|
+
### 3.2 다크 모드
|
|
120
|
+
|
|
121
|
+
다크 모드는 **시스템 설정 자동 추종 + 명시적 강제** 두 경로를 모두 지원한다.
|
|
122
|
+
|
|
123
|
+
- `@media (prefers-color-scheme: dark)` 안에서 `:root:not([data-theme='light'])`에 다크 토큰을 적용 → 시스템이 다크이고 사용자가 라이트로 고정하지 않았으면 다크.
|
|
124
|
+
- `:root[data-theme='dark']` → 사용자가 명시적으로 다크를 고른 경우.
|
|
125
|
+
- 즉 `data-theme` 속성(`light`/`dark`/없음)으로 사용자 의도가 시스템 설정을 덮어쓴다.
|
|
126
|
+
|
|
127
|
+
다크 모드는 같은 토큰의 값만 교체하므로 컴포넌트 CSS는 변경 없이 그대로 동작한다. 이것이 토큰 우선 설계의 핵심 이득이다.
|
|
128
|
+
|
|
129
|
+
### 3.3 상태 배지 (Badge)
|
|
130
|
+
|
|
131
|
+
`Badge(status, label)`(`lib.js`)는 `badge badge--<status>` 클래스를 가진 캡슐을 만든다. 작은 점(`.dot`) + 라벨 구성이다. 8개 상태가 시맨틱 색과 1:1로 매핑된다(`styles.css`).
|
|
132
|
+
|
|
133
|
+
| 상태 | 색 |
|
|
134
|
+
| --- | --- |
|
|
135
|
+
| `draft` | slate |
|
|
136
|
+
| `planned` | blue |
|
|
137
|
+
| `applied` | violet |
|
|
138
|
+
| `document_passed` | teal |
|
|
139
|
+
| `interview` | amber |
|
|
140
|
+
| `final_passed` | green |
|
|
141
|
+
| `rejected` | red |
|
|
142
|
+
| `on_hold` | slate |
|
|
143
|
+
|
|
144
|
+
색은 단계가 진행될수록 따뜻해지고, 합격은 초록, 불합격은 빨강이라는 직관을 따른다. 배지는 "대표 자기소개서/문서" 표시에도 재활용된다(`final_passed` 색으로 `대표` 라벨, `documents.js`).
|
|
145
|
+
|
|
146
|
+
### 3.4 칸반 보드
|
|
147
|
+
|
|
148
|
+
Applications 페이지의 보드는 `.board`(가로 스크롤, `grid-auto-flow: column`, 열 너비 272px)와 `.board__col` / `.board__col-head`(상태명 + 개수 카운트) / `.board__cards` / `.board-card`로 구성한다. 카드 한 장은 회사명·직무·메타(상태 배지 등)를 담는다. 열 높이는 `calc(100vh - 200px)`로 묶고 내부 스크롤한다. 8단계 상태가 그대로 보드의 열이 된다.
|
|
149
|
+
|
|
150
|
+
### 3.5 버전 타임라인
|
|
151
|
+
|
|
152
|
+
Documents의 자기소개서 상세는 **버전 기록**을 세로 타임라인으로 보여준다(`documents.js`의 `CoverDetailBody` → `VersionItem`). 마크업은 `.timeline` 안에 `.tl-item`(레일 점 `.tl-item__dot` + 연결선 `.tl-item__line` + 본문 `.tl-item__body`)을 쌓는 구조다.
|
|
153
|
+
|
|
154
|
+
- 버전은 `version_no` 내림차순으로 정렬(최신이 위).
|
|
155
|
+
- 현재 버전은 `.is-current`로 점이 악센트 색으로 강조되고, 헤더에 `현재 버전` 배지가 붙는다.
|
|
156
|
+
- 각 항목에 출처 칩(`직접 입력 / 파일 업로드 / AI 생성 / 직접 수정`), 변경 메모, 상대 시간이 표시된다.
|
|
157
|
+
- 항목 액션: `이 버전 보기`(상단 미리보기 교체), `현재 버전으로 지정`, `복사`.
|
|
158
|
+
|
|
159
|
+
미리보기(`.doc-preview`)는 현재 버전 본문을 기본 표시하고, "이 버전 보기"로 특정 버전을 끼워 볼 수 있다.
|
|
160
|
+
|
|
161
|
+
### 3.6 그 밖의 공용 컴포넌트
|
|
162
|
+
|
|
163
|
+
`lib.js`가 제공하는 컴포넌트만 사용하고, 스타일을 손으로 짜지 않는다. `Card`, `Stat`, `Btn`(`primary/ghost/danger`), `IconBtn`, `EmptyState`, `Chips`, `Field`/`Input`/`Textarea`/`Select`, `PageHead`, `openModal`/`confirmDialog`, `toast`/`toastOk`/`toastError`, `Spinner`(스켈레톤). 아이콘은 `icon(name)`으로 1.7px 스트로크 SVG 세트를 쓴다.
|
|
164
|
+
|
|
165
|
+
피드백 패턴도 통일돼 있다. 변경 작업은 `try/catch`로 감싸 성공 시 `toastOk`, 실패 시 `toastError`, 위험한 삭제는 `confirmDialog`로 한 번 더 묻는다.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## 4. XSS-safe 렌더링 원칙
|
|
170
|
+
|
|
171
|
+
이 원칙은 타협하지 않는다. 데이터(이력서·자기소개서 본문 등)는 사용자/AI가 채우므로, 신뢰할 수 없는 문자열로 취급한다.
|
|
172
|
+
|
|
173
|
+
`lib.js` 상단 주석이 규칙을 명문화한다.
|
|
174
|
+
|
|
175
|
+
> XSS safety: el() puts strings into textContent (never innerHTML). User content (résumé/cover-letter text) is always rendered as text. Only our own trusted icon SVG strings use innerHTML.
|
|
176
|
+
|
|
177
|
+
구체적으로:
|
|
178
|
+
|
|
179
|
+
- `el(tag, props, ...children)`에서 문자열·숫자 자식은 `document.createTextNode`로 들어간다(`appendChildren`). 따라서 본문에 `<script>`가 있어도 코드로 실행되지 않고 글자로 보인다.
|
|
180
|
+
- `innerHTML`은 오직 **신뢰된 호출자**만 쓴다. `props.html` 키와 `icon()`의 SVG 주입이 전부이며, 이는 우리가 만든 고정 문자열이다. DB/사용자 문자열을 `html:`로 넘기지 않는다.
|
|
181
|
+
- 자기소개서·이력서 본문은 `.doc-preview` 요소에 텍스트로 넣는다. CSS가 `white-space: pre-wrap; word-break: break-word`를 처리하므로 줄바꿈·서식이 살아 있으면서도 안전하다(`documents.js`의 미리보기, 버전 보기).
|
|
182
|
+
|
|
183
|
+
새 페이지를 만들 때도 [`UI_CONTRACT.md`](../apps/web/UI_CONTRACT.md)가 같은 점을 강조한다: *"never use innerHTML with user/DB content."*
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 5. 직접 수정 → 새 버전 저장 UX
|
|
188
|
+
|
|
189
|
+
자기소개서는 **덮어쓰지 않고 버전을 쌓는다**는 것이 핵심 UX다. 사용자가 손으로 고친 결과도 사라지지 않고 기록으로 남는다.
|
|
190
|
+
|
|
191
|
+
흐름(`documents.js`의 `EditNewVersion`):
|
|
192
|
+
|
|
193
|
+
1. 상세 모달에서 `수정하여 새 버전 저장` 버튼을 누르면 편집 영역이 펼쳐지고, 현재 버전 본문이 편집창에 채워진다.
|
|
194
|
+
2. 내용을 고치고 선택적으로 변경 메모(예: `지원동기 보강, 문장 다듬기`)를 적는다.
|
|
195
|
+
3. `새 버전으로 저장`을 누르면 `POST /api/cover-letters/{id}/versions`로 새 버전이 생성된다. 이때 출처는 `source: 'edit'`(`직접 수정`)으로 기록된다.
|
|
196
|
+
4. 저장 후 타임라인이 갱신되고, 방금 저장한 버전이 최신으로 올라온다.
|
|
197
|
+
|
|
198
|
+
이 설계의 의도:
|
|
199
|
+
|
|
200
|
+
- **이력 보존** — AI가 생성한 버전과 사람이 고친 버전이 출처 라벨로 구분되어 한 타임라인에 공존한다.
|
|
201
|
+
- **되돌리기 가능** — 어떤 버전이든 `현재 버전으로 지정`(`PUT .../current-version`)으로 다시 활성화할 수 있다. 직접 수정이 곧 파괴가 아니다.
|
|
202
|
+
- **내보내기 분리** — 보관·열람과 별개로 MD/HTML 내보내기(`/api/export/cover-letter/{id}`)는 읽기 전용 GET이라 토큰 없이 동작한다.
|
|
203
|
+
|
|
204
|
+
문서(이력서·경력기술서)는 버전 타임라인 대신 단일 본문 수정(`PUT /api/documents/{id}`) 모델을 쓴다. 자기소개서만 버전 누적이 의미 있다고 보고 차등 적용했다.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## 6. 접근성
|
|
209
|
+
|
|
210
|
+
복잡한 ARIA를 쌓기보다, 시맨틱 마크업과 키보드·포커스 기본기를 지킨다.
|
|
211
|
+
|
|
212
|
+
- **포커스 가시성**: `:focus-visible`에 `2px` 악센트 아웃라인(`outline-offset: 2px`). 키보드 사용자가 현재 위치를 항상 알 수 있다.
|
|
213
|
+
- **아이콘 버튼 라벨**: `IconBtn`은 `aria-label`을 자동으로 붙인다(`title || name`). 글자가 없는 버튼도 스크린리더가 읽는다.
|
|
214
|
+
- **모달 키보드**: `openModal`은 `Escape`로 닫히고, 열릴 때 첫 입력 요소(`input,textarea,select,button`)에 포커스를 준다. 스크림 클릭으로도 닫힌다.
|
|
215
|
+
- **색에만 의존하지 않음**: 상태 배지는 색뿐 아니라 라벨 텍스트와 점을 함께 제공한다. 점수도 색(`score-strong/mid/weak`)과 숫자(`N점`)를 같이 보여준다.
|
|
216
|
+
- **시맨틱 요소**: 표는 `table/thead/th/tbody`, 제목은 `h1~h4`, 폼은 `label`과 컨트롤을 `Field`로 묶는다.
|
|
217
|
+
- **시스템 테마 존중**: `prefers-color-scheme`를 따라가므로 OS의 다크 선호를 자동 반영한다.
|
|
218
|
+
|
|
219
|
+
라이브 영역(toast 등)과 포커스 트랩 등은 추후 보강 여지가 있는 지점으로 남겨 둔다.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## 7. 참고 서비스의 영향과 차별점
|
|
224
|
+
|
|
225
|
+
`styles.css` 주석이 밝히듯 **Linear / Notion / Raycast의 절제**에서 톤을 빌렸다.
|
|
226
|
+
|
|
227
|
+
| 서비스 | 빌려온 것 |
|
|
228
|
+
| --- | --- |
|
|
229
|
+
| **Linear** | 조밀한 정보 밀도, 키보드 친화, 차분한 단색 악센트, 상태 칸반 |
|
|
230
|
+
| **Notion** | 중립 팔레트, 부드러운 카드·경계선, 콘텐츠 우선의 차분한 표면 |
|
|
231
|
+
| **Raycast** | 군더더기 없는 표면, 빠른 액션, "도구다움"의 미니멀함 |
|
|
232
|
+
|
|
233
|
+
차별점:
|
|
234
|
+
|
|
235
|
+
- **완전 오프라인·로컬 전용**: 프레임워크·CDN 없이 동작하고(`styles.css` 주석: *"works fully offline; nothing leaves the machine"*), 모든 데이터가 로컬에만 남는다. 위 서비스들이 클라우드 SaaS인 것과 정반대다.
|
|
236
|
+
- **MCP-우선, LLM 내장 없음**: 화면은 AI가 채우지만, 제품 자체는 "AI 앱"으로 보이지 않게 의도적으로 절제했다(§1.2). 분석·작성은 사용자의 AI가, 보관·구조화·열람은 CareerMate가 맡는다.
|
|
237
|
+
- **단일 도메인 특화**: 범용 노트(Notion)나 이슈 트래커(Linear)가 아니라, "취업 준비 한 사이클"(공고 → 적합도 → 자기소개서 → 지원 상태 → 면접)에 정확히 맞춘 정보 구조와 상태 모델을 갖는다.
|
|
238
|
+
- **빌드리스 바닐라**: 디자인 시스템이 CSS 변수 토큰 + 소수의 JS 컴포넌트로만 이뤄져, 번들러·런타임 의존성이 없다. 무거운 디자인 시스템 패키지 대신 `lib.js` 한 파일이 그 역할을 한다.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## 관련 문서
|
|
243
|
+
|
|
244
|
+
- 화면 작성 규칙: [`apps/web/UI_CONTRACT.md`](../apps/web/UI_CONTRACT.md)
|
|
245
|
+
- 참고 구현(Home): [`apps/web/public/pages/home.js`](../apps/web/public/pages/home.js)
|
|
246
|
+
- 디자인 토큰·스타일: [`apps/web/public/styles.css`](../apps/web/public/styles.css)
|
|
247
|
+
- 컴포넌트·API 클라이언트: [`apps/web/public/lib.js`](../apps/web/public/lib.js)
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# 워크플로우 (Workflows)
|
|
2
|
+
|
|
3
|
+
CareerMate는 내부에 LLM을 두지 않는다. 분석과 작성은 사용자의 AI(ChatGPT/Claude/Gemini)가 수행하고, 그 AI가 CareerMate의 로컬 MCP 도구를 호출해 로컬 커리어 DB를 읽고 쓴다. 이 문서는 **AI가 따라야 하는 표준 작업 절차(워크플로우)** 를 정리한다.
|
|
4
|
+
|
|
5
|
+
워크플로우 정의는 코드 안에 데이터로 인코딩되어 있으며(`packages/workflows/src/definitions.ts`), 각 단계는 호출할 구체적인 MCP 도구 이름을 직접 참조한다. AI는 `get_workflow_guide` MCP 도구로 이 정의를 그대로 읽어올 수 있다. 본 문서는 그 정의(`WORKFLOWS` 배열)와 일치한다.
|
|
6
|
+
|
|
7
|
+
관련 문서:
|
|
8
|
+
|
|
9
|
+
- 연결 직후 AI가 따라가는 시작 런북은 [시작 런북](./START_WORKFLOW.md)을 참고한다.
|
|
10
|
+
- 설치·연결 절차는 [설치 가이드](./INSTALL.md)를 참고한다.
|
|
11
|
+
- MCP 도구 24종의 입출력 명세는 코드(`packages/mcp-tools/src/tools.ts`)에 정의되어 있으며, AI는 각 도구의 `inputSchema`로 직접 확인할 수 있다.
|
|
12
|
+
|
|
13
|
+
## 워크플로우 5종 개요
|
|
14
|
+
|
|
15
|
+
| id | 제목 | 핵심 목적 |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| `onboarding` | 온보딩 (첫 셋업) | 프로필·이력서·자기소개서를 등록하고 대시보드를 연다. |
|
|
18
|
+
| `analyze_job` | 공고 분석 (적합도 분석) | 공고를 읽어 프로필과 비교하고, 공고·적합도 분석을 저장한 뒤 자기소개서 작성을 제안한다. |
|
|
19
|
+
| `write_cover_letter` | 자기소개서 작성 | 특정 공고에 맞춘 자기소개서를 사용자 확인을 거쳐 작성·버전 저장한다. |
|
|
20
|
+
| `manage_application_status` | 지원 상태 관리 | 지원 진행 상황(8단계)을 업데이트하고 다음 행동을 제안한다. |
|
|
21
|
+
| `prepare_interview` | 면접 준비 | 서류 합격 이후 예상 질문·STAR 답변·1분 자기소개를 준비·저장한다. |
|
|
22
|
+
|
|
23
|
+
## 공통 원칙
|
|
24
|
+
|
|
25
|
+
- **추측 금지**: 저장된 실제 경험·성과만 사용한다. 없는 경험을 지어내지 않으며, 빈칸이나 확인이 필요한 부분은 표시해 사용자에게 묻는다.
|
|
26
|
+
- **사용자 확인 우선**: 큰 작업(자기소개서 작성 등)을 진행하기 전에 대상·강조점·톤·분량을 확인한다.
|
|
27
|
+
- **쉬운 한국어 전달**: 분석·결과를 사용자에게 설명할 때는 기술 용어를 빼고 쉬운 한국어로 전달한다.
|
|
28
|
+
- **로컬 전용**: 모든 데이터는 로컬에만 저장된다.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 1. onboarding — 온보딩 (첫 셋업)
|
|
33
|
+
|
|
34
|
+
사용자가 처음 연결했을 때 프로필·이력서·자기소개서를 등록하고 대시보드를 여는 흐름.
|
|
35
|
+
|
|
36
|
+
**트리거:** 사용자가 처음 CareerMate에 연결했거나, "시작하자 / 셋업해줘 / 프로필 등록"이라고 요청할 때.
|
|
37
|
+
|
|
38
|
+
**단계:**
|
|
39
|
+
|
|
40
|
+
1. `get_onboarding_status`를 호출해 현재 등록 상태(`has_profile`, `has_resume`, `has_cover_letter`, `has_experience`, `has_skills`, `profile_completeness`, `next_steps`)를 확인한다.
|
|
41
|
+
2. 비어 있는 항목을 사용자에게 쉬운 한국어로 안내하고, 이력서/자기소개서 파일 업로드 또는 정보 입력을 요청한다.
|
|
42
|
+
3. 수집한 정보를 구조화해 `save_profile`로 저장한다(이름·연락처·`headline`·`summary`·`desired_roles`·`desired_conditions`, 그리고 글쓰기 선호인 `preferred_tone`과 `emphasis_points` 포함). 파일 출처는 `source=upload`, 직접 입력은 `manual`.
|
|
43
|
+
4. 업로드한 이력서/경력기술서/포트폴리오 본문을 `add_resume`로 저장한다(`kind`·`title` 지정, 대표 문서는 `is_primary=true`).
|
|
44
|
+
5. 기존 자기소개서가 있으면 `add_cover_letter`로 저장한다(없으면 나중에 작성 가능함을 안내).
|
|
45
|
+
6. `open_dashboard`를 호출해 사용자가 자기 데이터를 확인하게 한다.
|
|
46
|
+
7. 다시 `get_onboarding_status`로 완성도를 확인하고, 다음 단계(공고 분석 등)를 제안한다.
|
|
47
|
+
|
|
48
|
+
**도구 호출 순서 요약:**
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
get_onboarding_status
|
|
52
|
+
→ save_profile
|
|
53
|
+
→ add_resume
|
|
54
|
+
→ add_cover_letter (선택)
|
|
55
|
+
→ open_dashboard
|
|
56
|
+
→ get_onboarding_status (재확인)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 2. analyze_job — 공고 분석 (적합도 분석)
|
|
62
|
+
|
|
63
|
+
채용 공고를 읽어 사용자 프로필과 비교하고, 공고와 적합도 분석을 저장한 뒤 자기소개서 작성을 제안하는 **핵심 흐름**이다.
|
|
64
|
+
|
|
65
|
+
**트리거:** 사용자가 채용 공고 링크나 본문을 붙여넣거나 "이 공고 분석해줘 / 나랑 잘 맞아?"라고 물을 때.
|
|
66
|
+
|
|
67
|
+
**단계:**
|
|
68
|
+
|
|
69
|
+
1. **공고 읽기** — 공고 원문(붙여넣은 텍스트 또는 링크 내용)을 읽고 `company`, `position`, `requirements`, `keywords`, `deadline` 등 핵심 정보를 정리한다.
|
|
70
|
+
2. **컨텍스트 수집** — `get_application_context`를 호출해 한 번에 프로필·대표 이력서·경력·프로젝트·스킬·기존 자기소개서·최근 지원 현황·이전 적합도 분석·같은 회사/직무 관련 기록·글쓰기 선호(`preferred_tone`, `emphasis_points`)를 가져온다. (분석 대상 공고가 이미 저장돼 있으면 `job_id`도 함께 전달한다.)
|
|
71
|
+
3. **비교/분석** — 공고 요구사항과 사용자 경력/스킬/프로젝트를 비교한다. 일치하는 강점(`strengths`)과 부족한 부분(`gaps`), 매칭 키워드(`matched_keywords`)와 누락 키워드(`missing_keywords`)를 도출하고, 종합 적합도 점수(`score` 0~100)와 요약(`summary`), 지원 전략 제안(`recommendations`)을 정리한다. **없는 경험을 지어내지 않는다.**
|
|
72
|
+
4. **공고 저장** — 아직 저장되지 않은 공고라면 `save_job_posting`으로 공고를 저장하고 `job_id`를 확보한다.
|
|
73
|
+
5. **분석 저장** — 도출한 분석 결과를 `save_fit_analysis`로 저장한다(`job_id`, `score`, `summary`, `strengths`, `gaps`, `matched_keywords`, `missing_keywords`, `recommendations`).
|
|
74
|
+
6. **사용자 설명** — 분석 결과를 사용자에게 쉬운 한국어로 설명한다(잘 맞는 점, 보완할 점, 추천 전략). 기술 용어는 빼고 전달한다.
|
|
75
|
+
7. **자소서 제안** — "이 공고에 맞춘 자기소개서를 써드릴까요?"라고 자기소개서 작성을 제안한다.
|
|
76
|
+
8. **동의 시 자소서 저장** — 사용자가 동의하면 적합도 분석과 글쓰기 선호를 반영해 자기소개서를 작성하고 `save_cover_letter_version`으로 저장한다(`job_id` 연결, `note`에 작성 의도 요약).
|
|
77
|
+
9. **지원 건 열기** — `open_application`으로 해당 지원 건을 열어 사용자가 결과를 확인하게 하고, 지원 상태 변경(예: 지원 예정/지원 완료)을 제안한다.
|
|
78
|
+
|
|
79
|
+
**도구 호출 순서 요약(정식 흐름):**
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
(공고 원문 읽기)
|
|
83
|
+
→ get_application_context # job_id 있으면 함께 전달
|
|
84
|
+
→ (비교/분석: strengths·gaps·키워드·score·summary·recommendations)
|
|
85
|
+
→ save_job_posting # 미저장 공고일 때만, job_id 확보
|
|
86
|
+
→ save_fit_analysis # job_id 연결
|
|
87
|
+
→ (사용자에게 쉬운 한국어로 설명)
|
|
88
|
+
→ (자기소개서 작성 제안)
|
|
89
|
+
→ save_cover_letter_version # 사용자 동의 시에만, job_id·note
|
|
90
|
+
→ open_application # 결과 확인 + 상태 변경 제안
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
> 핵심 도구는 `get_application_context`이다. 프로필·이력서·경력·프로젝트·스킬·기존 자소서·지원 현황·이전 분석·관련 기록·글쓰기 선호를 한 번에 가져오므로, 분석/작성에 필요한 데이터를 여러 번 나눠 조회할 필요가 없다.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 3. write_cover_letter — 자기소개서 작성
|
|
98
|
+
|
|
99
|
+
특정 공고에 맞춘 자기소개서를 사용자 확인을 거쳐 작성하고 버전으로 저장하는 흐름.
|
|
100
|
+
|
|
101
|
+
**트리거:** 사용자가 "자기소개서 써줘 / 이 공고용 자소서 만들어줘"라고 요청하거나, 공고 분석 후 작성 제안에 동의했을 때.
|
|
102
|
+
|
|
103
|
+
**단계:**
|
|
104
|
+
|
|
105
|
+
1. `get_application_context`(가능하면 `job_id` 포함)를 호출해 프로필·경력·프로젝트·스킬·기존 자기소개서·이전 적합도 분석·글쓰기 선호(`preferred_tone`, `emphasis_points`)를 가져온다.
|
|
106
|
+
2. 작성 전에 대상 공고, 강조할 포인트, 톤, 분량을 사용자에게 확인한다(확인 없이 큰 작업을 진행하지 않는다).
|
|
107
|
+
3. 저장된 실제 경험·성과만으로 자기소개서를 작성한다. 빈칸이나 확인이 필요한 부분은 추측하지 말고 표시해 사용자에게 묻는다.
|
|
108
|
+
4. 완성본을 `save_cover_letter_version`으로 저장한다(기존 자기소개서면 `cover_letter_id`, 새로 만들면 `title`, 공고용이면 `job_id`, 변경 요약은 `note`, `source=ai`).
|
|
109
|
+
5. 사용자가 원하면 `export_cover_letter`로 파일로 내보내고, 추가 수정 요청이 있으면 새 버전으로 다시 저장한다.
|
|
110
|
+
6. 저장 결과를 쉬운 한국어로 알리고, 지원 상태 업데이트(`update_application_status`)나 면접 준비 등 다음 단계를 제안한다.
|
|
111
|
+
|
|
112
|
+
**도구 호출 순서 요약:**
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
get_application_context # job_id 있으면 함께 전달
|
|
116
|
+
→ (대상·강조점·톤·분량 확인)
|
|
117
|
+
→ save_cover_letter_version # cover_letter_id 또는 title, job_id, note, source=ai
|
|
118
|
+
→ export_cover_letter (선택)
|
|
119
|
+
→ (다음 단계 제안: update_application_status 등)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 4. manage_application_status — 지원 상태 관리
|
|
125
|
+
|
|
126
|
+
지원 진행 상황(8단계)을 업데이트하고 상태에 맞는 다음 행동을 제안하는 흐름.
|
|
127
|
+
|
|
128
|
+
**트리거:** 사용자가 "지원했어 / 서류 합격했어 / 면접 잡혔어 / 떨어졌어"처럼 진행 상황을 알리거나 상태 변경을 요청할 때.
|
|
129
|
+
|
|
130
|
+
**단계:**
|
|
131
|
+
|
|
132
|
+
1. 필요하면 `get_application_context` 또는 `get_job_posting`으로 대상 지원 건과 공고를 확인한다.
|
|
133
|
+
2. 적절한 상태로 `update_application_status`를 호출한다. 8단계: `draft`(작성 중), `planned`(지원 예정), `applied`(지원 완료), `document_passed`(서류 합격), `interview`(면접 진행), `final_passed`(최종 합격), `rejected`(불합격), `on_hold`(보류). 변경 사유는 `note`에 남긴다.
|
|
134
|
+
3. 변경 결과를 사용자에게 쉬운 한국어로 알린다.
|
|
135
|
+
4. 상태에 맞는 다음 단계를 제안한다:
|
|
136
|
+
- `applied` → 결과 기다리기 안내
|
|
137
|
+
- `document_passed` / `interview` → 면접 준비(`prepare_interview`) 제안
|
|
138
|
+
- `final_passed` → 축하 및 마무리
|
|
139
|
+
- `rejected` / `on_hold` → 회고 또는 다른 공고 탐색 제안
|
|
140
|
+
5. 필요하면 `open_application`으로 해당 지원 건을 열어 보여준다.
|
|
141
|
+
|
|
142
|
+
**상태 전이 참고:** `document_passed` 이상에서 면접 준비가 해금된다. 8단계 상태값은 코드(`update_application_status`의 `inputSchema`)에 정의되어 있다.
|
|
143
|
+
|
|
144
|
+
**도구 호출 순서 요약:**
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
get_application_context | get_job_posting (필요 시)
|
|
148
|
+
→ update_application_status # 8단계 중 하나, note에 사유
|
|
149
|
+
→ (상태별 다음 단계 제안)
|
|
150
|
+
→ open_application (선택)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 5. prepare_interview — 면접 준비
|
|
156
|
+
|
|
157
|
+
서류 합격 이후 예상 질문·STAR 답변·1분 자기소개를 준비해 저장하는 흐름.
|
|
158
|
+
|
|
159
|
+
**트리거:** 지원 상태가 서류 합격(`document_passed`) 또는 면접 진행(`interview`)으로 바뀌었을 때, 또는 사용자가 "면접 준비 도와줘"라고 요청할 때.
|
|
160
|
+
|
|
161
|
+
**단계:**
|
|
162
|
+
|
|
163
|
+
1. `get_application_context`(`job_id` 포함)를 호출해 공고, 프로필, 경력/프로젝트, 적합도 분석(강점·갭), 자기소개서를 가져온다.
|
|
164
|
+
2. 공고와 사용자 경험을 바탕으로 예상 면접 질문(`question`, `intent`, `followups`, `answer_outline`)을 도출한다.
|
|
165
|
+
3. 핵심 경험에 대한 STAR 가이드(`question`, `situation`, `task`, `action`, `result`)와 1분 자기소개(`self_introduction`) 초안을 작성한다. **실제 경험만 사용한다.**
|
|
166
|
+
4. 준비 내용을 `save_interview_prep`로 저장한다(`job_id`, `questions`, `star_guides`, `self_introduction`, `notes`).
|
|
167
|
+
5. 준비 내용을 사용자에게 쉬운 한국어로 요약해 전달하고, 모의 면접/추가 질문 연습이나 상태 업데이트(`update_application_status`로 `interview`/`final_passed`)를 제안한다.
|
|
168
|
+
|
|
169
|
+
**도구 호출 순서 요약:**
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
get_application_context # job_id 포함
|
|
173
|
+
→ (예상 질문 도출: question·intent·followups·answer_outline)
|
|
174
|
+
→ (STAR 가이드 + 1분 자기소개 초안)
|
|
175
|
+
→ save_interview_prep # job_id, questions, star_guides, self_introduction, notes
|
|
176
|
+
→ (요약 전달 + 모의 면접/상태 업데이트 제안)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 워크플로우 간 연결
|
|
182
|
+
|
|
183
|
+
워크플로우는 독립적이지 않고 자연스럽게 이어진다.
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
onboarding
|
|
187
|
+
→ analyze_job ──(자소서 제안 동의)──→ write_cover_letter
|
|
188
|
+
│
|
|
189
|
+
└─→ manage_application_status ──(document_passed 이상)──→ prepare_interview
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
- `analyze_job`의 마지막 단계는 자기소개서 작성 제안으로 이어지며, 동의 시 `write_cover_letter`의 작업(=`save_cover_letter_version`)을 수행한다.
|
|
193
|
+
- `manage_application_status`에서 상태가 `document_passed`/`interview`가 되면 `prepare_interview`를 제안한다.
|
|
194
|
+
- 모든 흐름에서 데이터 조회의 중심은 `get_application_context`이며, 작성/분석에 필요한 컨텍스트를 한 번에 제공한다.
|
|
195
|
+
|
|
196
|
+
## 코드와의 대응
|
|
197
|
+
|
|
198
|
+
- 워크플로우 정의: `packages/workflows/src/definitions.ts` (`WORKFLOWS: WorkflowDefinition[]`)
|
|
199
|
+
- 조회/렌더 헬퍼: `packages/workflows/src/index.ts` (`getWorkflow(id)`, `renderWorkflowMarkdown(id)`)
|
|
200
|
+
- AI는 `get_workflow_guide` MCP 도구로 위 정의를 마크다운으로 렌더링해 인라인으로 읽을 수 있다.
|