careermate 0.1.1 → 0.1.2
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 +12 -10
- package/THIRD_PARTY_NOTICES.md +15 -0
- package/apps/mcp/src/index.ts +4 -5
- package/apps/web/demo/demo-shim.js +536 -0
- package/apps/web/demo/demo.css +75 -0
- package/apps/web/demo/seed.js +269 -0
- package/apps/web/public/app.js +34 -2
- package/apps/web/public/index.html +10 -2
- package/apps/web/public/lib.js +134 -22
- package/apps/web/public/pages/applications.js +2 -9
- package/apps/web/public/pages/documents.js +66 -50
- package/apps/web/public/pages/home.js +12 -11
- package/apps/web/public/pages/interview.js +9 -12
- package/apps/web/public/pages/jobs.js +39 -36
- package/apps/web/public/pages/profile.js +66 -86
- package/apps/web/public/pages/settings.js +15 -10
- package/apps/web/public/styles.css +56 -2
- package/apps/web/src/http.ts +31 -4
- package/apps/web/src/info.ts +3 -3
- package/apps/web/src/routes.ts +2 -1
- package/apps/web/src/server.ts +1 -4
- package/dist/doctor.mjs +72607 -611
- package/dist/install-page/.omc/state/agent-replay-55a58c0d-bb6e-43ea-8a05-4b6c93b80b34.jsonl +12 -0
- package/dist/install-page/.omc/state/idle-notif-cooldown.json +3 -0
- package/dist/install-page/.omc/state/sessions/55a58c0d-bb6e-43ea-8a05-4b6c93b80b34/mission-state.json +147 -0
- package/dist/install-page/.omc/state/sessions/55a58c0d-bb6e-43ea-8a05-4b6c93b80b34/pre-tool-advisory-throttle.json +22 -0
- package/dist/install-page/.omc/state/sessions/55a58c0d-bb6e-43ea-8a05-4b6c93b80b34/subagent-tracking-state.json +44 -0
- package/dist/install-page/index.html +31 -70
- package/dist/install-page/landing.js +54 -0
- package/dist/install-page/start.html +13 -36
- package/dist/install-page/style.css +34 -1
- package/dist/mcp.mjs +73936 -1976
- package/dist/migrate.mjs +17 -10
- package/dist/pdf.worker.min.mjs +28 -0
- package/dist/public/app.js +34 -2
- package/dist/public/index.html +10 -2
- package/dist/public/lib.js +134 -22
- package/dist/public/pages/applications.js +2 -9
- package/dist/public/pages/documents.js +66 -50
- package/dist/public/pages/home.js +12 -11
- package/dist/public/pages/interview.js +9 -12
- package/dist/public/pages/jobs.js +39 -36
- package/dist/public/pages/profile.js +66 -86
- package/dist/public/pages/settings.js +15 -10
- package/dist/public/styles.css +56 -2
- package/dist/web.mjs +39631 -244
- package/docs/ARCHITECTURE.md +5 -5
- package/docs/DECISIONS.md +1 -1
- package/docs/DEMO.md +3 -3
- package/docs/MCP_TOOLS.md +49 -9
- package/docs/ROADMAP.md +8 -7
- package/docs/START_WORKFLOW.md +13 -7
- package/docs/TODO.md +1 -1
- package/docs/WORKFLOWS.md +7 -5
- package/docs/superpowers/specs/2026-06-16-demo-dashboard-deploy-design.md +64 -0
- package/package.json +6 -2
- package/packages/db/src/paths.ts +11 -0
- package/packages/mcp-tools/src/bridge.ts +55 -32
- package/packages/mcp-tools/src/index.ts +9 -0
- package/packages/mcp-tools/src/tools.ts +193 -70
- package/packages/mcp-tools/src/update.ts +330 -0
- package/packages/parsers/src/documents.ts +521 -0
- package/packages/parsers/src/index.ts +8 -5
- package/packages/prompts/src/onboarding.ts +2 -2
- package/packages/shared/src/build-info.ts +22 -0
- package/packages/shared/src/index.ts +1 -0
- package/packages/shared/src/schemas.ts +24 -3
- package/packages/workflows/src/definitions.ts +4 -2
- package/scripts/build-demo.mjs +130 -0
- package/scripts/build-dist.mjs +20 -1
- package/scripts/doctor.ts +16 -3
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
CareerMate는 독립적으로 동작하는 **두 개의 로컬 프로세스**로 구성된다.
|
|
12
12
|
|
|
13
13
|
- **대시보드 웹서버** (`apps/web`) — `npm start`로 사용자가 직접 기동. `http://127.0.0.1:4319`(사용 중이면 다음 포트로 폴백)에 바인딩하고 브라우저를 자동으로 연다. 사람이 보는 UI와 REST API를 제공한다.
|
|
14
|
-
- **MCP stdio 서버** (`apps/mcp`) — AI 클라이언트(Claude Desktop, ChatGPT, Cursor 등)가 `npm run mcp` 형태로 기동. stdio 위에서 MCP 프로토콜로 통신하며,
|
|
14
|
+
- **MCP stdio 서버** (`apps/mcp`) — AI 클라이언트(Claude Desktop, ChatGPT, Cursor 등)가 `npm run mcp` 형태로 기동. stdio 위에서 MCP 프로토콜로 통신하며, 31개의 CareerMate 도구를 노출한다.
|
|
15
15
|
|
|
16
16
|
두 프로세스는 서로를 직접 호출하지 않는다. 대신 **같은 로컬 SQLite 파일**(`~/.careermate/careermate.sqlite`)을 공유한다. AI가 도구로 쓴 내용은 대시보드에 즉시 보이고, 사용자가 대시보드에서 편집한 내용은 AI가 다음 도구 호출에서 그대로 읽는다. SQLite는 WAL(Write-Ahead Logging) 모드로 열려 두 프로세스의 동시 읽기/쓰기를 안전하게 처리한다.
|
|
17
17
|
|
|
@@ -28,7 +28,7 @@ CareerMate는 독립적으로 동작하는 **두 개의 로컬 프로세스**로
|
|
|
28
28
|
│ │ 대시보드 웹서버 │ │ MCP stdio 서버 │ │
|
|
29
29
|
│ │ (npm start) │ │ (npm run mcp, │ │
|
|
30
30
|
│ │ │ │ AI 클라이언트가 기동) │ │
|
|
31
|
-
│ │ 보안 게이트 → API 라우터 │ │
|
|
31
|
+
│ │ 보안 게이트 → API 라우터 │ │ 31개 도구 등록 │ │
|
|
32
32
|
│ │ → 정적 대시보드/설치페이지│ │ toCallToolResult │ │
|
|
33
33
|
│ └───────────┬──────────────┘ └───────────┬──────────────┘ │
|
|
34
34
|
│ │ │ │
|
|
@@ -72,7 +72,7 @@ CareerMate는 독립적으로 동작하는 **두 개의 로컬 프로세스**로
|
|
|
72
72
|
┌───────────────────────────┬───────────────────────────┐
|
|
73
73
|
│ │ │
|
|
74
74
|
apps/web (REST API) packages/mcp-tools │
|
|
75
|
-
· routes.ts ·
|
|
75
|
+
· routes.ts · 31개 도구 정의 │
|
|
76
76
|
· security/http/exports · 핸들러 → core 호출 │
|
|
77
77
|
└──────────┬────────────────┘
|
|
78
78
|
▼
|
|
@@ -190,7 +190,7 @@ CareerMate의 두 표면(MCP 도구, REST API)은 각각 다른 클라이언트
|
|
|
190
190
|
| `packages/shared` | `@careermate/shared` | 타입, zod 입력 스키마, 상태/문서 라벨·순서 등 상수. 모든 계층이 공유. | (없음) |
|
|
191
191
|
| `packages/db` | `@careermate/db` | 단일 `DatabaseSync` 연결(WAL), 12개 테이블 스키마/마이그레이션, 리포지토리, 데이터 경로(`paths.ts`), 런타임 핸드셰이크(`runtime.ts`). | shared |
|
|
192
192
|
| `packages/core` | `@careermate/core` | 비즈니스 유스케이스. `getApplicationContext`(핵심), `saveProfile`, `saveJobPosting`, `saveFitAnalysis`, `saveCoverLetterVersion`, `updateApplicationStatus`, `saveInterviewPrep`, 온보딩/요약/활동 조회 등. 두 진입점이 공유. | db, shared |
|
|
193
|
-
| `packages/mcp-tools` | `@careermate/mcp-tools` |
|
|
193
|
+
| `packages/mcp-tools` | `@careermate/mcp-tools` | 31개 MCP 도구 정의(`TOOLS`)와 핸들러, `toCallToolResult`. 도구 핸들러는 core를 호출. | core, shared, parsers/prompts/workflows |
|
|
194
194
|
| `packages/parsers` | `@careermate/parsers` | 공고 텍스트 정제(`cleanJobPosting`), 업로드 파일 텍스트 추출(`extractText`). | shared |
|
|
195
195
|
| `packages/exporters` | `@careermate/exporters` | md/html/txt 내보내기 포맷팅(`ExportResult`). | shared |
|
|
196
196
|
| `packages/prompts` | `@careermate/prompts` | 사용자 AI에 전달할 프롬프트 텍스트(`PROMPTS`). | shared |
|
|
@@ -204,5 +204,5 @@ CareerMate의 두 표면(MCP 도구, REST API)은 각각 다른 클라이언트
|
|
|
204
204
|
|
|
205
205
|
- 기술 의사결정 근거 및 보안 모델: [DECISIONS.md](./DECISIONS.md)
|
|
206
206
|
- 데이터 모델(테이블 12개): [DATA_MODEL.md](./DATA_MODEL.md)
|
|
207
|
-
- MCP 도구 목록(
|
|
207
|
+
- MCP 도구 목록(31개): [MCP_TOOLS.md](./MCP_TOOLS.md)
|
|
208
208
|
- 워크플로우: [WORKFLOWS.md](./WORKFLOWS.md)
|
package/docs/DECISIONS.md
CHANGED
|
@@ -140,7 +140,7 @@ CareerMate 내부에는 LLM을 두지 않는다. 대신 MCP(Model Context Protoc
|
|
|
140
140
|
- 데이터 흐름의 통제권이 사용자에게 있다(어떤 AI를 쓸지 사용자가 선택).
|
|
141
141
|
- "인터페이스 = 대화, 저장소 = 로컬 DB"라는 역할 분리가 명확하다.
|
|
142
142
|
- 트레이드오프: CareerMate 단독으로는 분석을 못 한다. MCP를 지원하는 AI 클라이언트가
|
|
143
|
-
전제된다. 도구 표면(
|
|
143
|
+
전제된다. 도구 표면(31개 MCP 도구)과 워크플로 가이드 설계가 제품 품질의 핵심이 된다.
|
|
144
144
|
|
|
145
145
|
---
|
|
146
146
|
|
package/docs/DEMO.md
CHANGED
|
@@ -95,14 +95,14 @@ npm start
|
|
|
95
95
|
- **글쓰기 선호도**를 꼭 채우세요: 선호 문체(`preferred_tone`)와 강조 포인트(`emphasis_points`). 이 두 값이 이후 자기소개서 품질에 직접 영향을 줍니다.
|
|
96
96
|
- 경력·프로젝트·기술도 이 화면에서 추가할 수 있습니다.
|
|
97
97
|
|
|
98
|
-
> 참고: 같은 작업을 AI에게 맡길 수도 있습니다. 그 경우 AI가 `save_profile
|
|
98
|
+
> 참고: 같은 작업을 AI에게 맡길 수도 있습니다. 그 경우 AI가 `save_profile`을 호출합니다. 전달한 필드만 갱신되고 나머지는 유지됩니다(부분 저장 안전).
|
|
99
99
|
|
|
100
100
|
### 4단계 — 자기소개서/이력서 추가 🧑
|
|
101
101
|
|
|
102
102
|
- 가진 이력서가 있으면 Documents 페이지에서 추가하고, 대표 문서로 지정합니다(대표 이력서는 이후 분석의 근거로 쓰입니다).
|
|
103
103
|
- 기존 자기소개서가 있으면 함께 등록해 두면 좋습니다(AI가 톤·표현을 참고).
|
|
104
104
|
|
|
105
|
-
> AI에게 맡기면 `add_resume`(이력서/경력기술서/포트폴리오), `
|
|
105
|
+
> AI에게 맡기면 `add_resume`(이력서/경력기술서/포트폴리오), `save_cover_letter_version`(기존 자소서는 title+content로 v1 저장)을 호출하고, 구조화된 경력·프로젝트·스킬은 `add_experience`·`add_project`·`add_skill`로 저장합니다.
|
|
106
106
|
|
|
107
107
|
### 5단계 — AI 클라이언트에서 MCP 연결 확인 🧑
|
|
108
108
|
|
|
@@ -143,7 +143,7 @@ npm run mcp
|
|
|
143
143
|
|
|
144
144
|
### 9단계 — AI가 공고를 구조화해 저장 🤖
|
|
145
145
|
|
|
146
|
-
- 🤖
|
|
146
|
+
- 🤖 붙여넣은 원문에서 회사명/직무/마감일/키워드를 AI가 직접 구조화합니다.
|
|
147
147
|
- 🤖 `save_job_posting`으로 저장합니다. `company`·`position`은 필수, 원문은 `description`, 자격/우대는 `requirements`, 키워드는 `keywords`에 넣습니다. 저장하면 **지원(application) 항목이 자동 생성**됩니다. 같은 `url`이면 중복 없이 갱신됩니다.
|
|
148
148
|
|
|
149
149
|
### 10단계 — AI가 지원 맥락을 가져옴 (★핵심) 🤖
|
package/docs/MCP_TOOLS.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
CareerMate는 **MCP-우선** 로컬 커리어 관리 도구입니다. 사용자는 자신의 ChatGPT/Claude/Gemini와 대화하고, 그 AI가 여기 정의된 MCP 도구를 호출해 로컬 커리어 DB(`~/.careermate/careermate.sqlite`)를 읽고 씁니다. **CareerMate 내부에는 LLM이 없습니다.** 적합도 분석·자기소개서 작성 같은 추론은 모두 사용자의 AI가 수행하고, CareerMate는 데이터를 저장하고 다시 돌려주는 역할만 합니다.
|
|
4
4
|
|
|
5
|
-
이 문서는 `packages/mcp-tools/src/tools.ts`에 정의된 **
|
|
5
|
+
이 문서는 `packages/mcp-tools/src/tools.ts`에 정의된 커리어 워크플로 **31개 도구**의 레퍼런스입니다. 도구의 입력 파라미터는 `packages/shared/src/schemas.ts`의 zod 스키마와 일치합니다.
|
|
6
6
|
|
|
7
7
|
> 설계 원칙: 이 도구들은 단순 CRUD 래퍼가 아닙니다. 각 도구는 AI가 사용자를 대신해 수행하는 실제 작업에 대응하며, 설명에 *언제* 그리고 *어떻게* 호출해야 하는지가 담겨 있습니다. 가장 중요한 도구는 [`get_application_context`](#get_application_context-) 입니다.
|
|
8
8
|
|
|
@@ -16,7 +16,36 @@ CareerMate는 **MCP-우선** 로컬 커리어 관리 도구입니다. 사용자
|
|
|
16
16
|
|
|
17
17
|
| 읽기전용(✅) 도구 | 쓰기/부수효과(✏️) 도구 |
|
|
18
18
|
| --- | --- |
|
|
19
|
-
| `
|
|
19
|
+
| `read_document`, `read_inbox`, `get_onboarding_status`, `get_profile`, `get_resumes`, `get_experiences`, `get_projects`, `get_skills`, `get_cover_letters`, `get_job_posting`, `list_jobs`, `get_application_context`, `list_recent_activity`, `get_workflow_guide`, `get_writing_style_guide`, `check_for_update` | `open_inbox`, `save_profile`, `add_resume`, `add_experience`, `add_project`, `add_skill`, `save_cover_letter_version`, `save_job_posting`, `save_fit_analysis`, `update_application_status`, `save_interview_prep`, `export_cover_letter`, `open_dashboard`, `open_application`, `update_careermate` |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 0. 문서 읽기 (Read Document)
|
|
24
|
+
|
|
25
|
+
| 도구 | 목적 | 주요 입력 | 반환 | 언제 호출 | 읽기전용 |
|
|
26
|
+
| --- | --- | --- | --- | --- | --- |
|
|
27
|
+
| `read_document` | 컴퓨터의 문서 파일에서 본문 텍스트 추출(PDF·Word·한컴·텍스트) | `path`(절대 경로 권장), `max_chars?` | `{ path, format, characters, truncated, warnings[], text }` | 사용자가 파일 경로를 주며 "이 파일 읽어줘/참고해줘"라고 할 때 | ✅ |
|
|
28
|
+
|
|
29
|
+
- 지원 형식: `.pdf`, `.docx`, `.hwp`(HWP 5.x), `.hwpx`, `.txt`/`.md` 등 텍스트. 전부 **순수 JS로 추출**하며 외부 변환기(poppler 등) 설치가 필요 없습니다.
|
|
30
|
+
- 추출한 텍스트는 그대로 새 파일로 다시 쓰지 말고, 분석/작성에 활용한 뒤 결과를 `add_resume`·`save_job_posting`·`save_cover_letter_version` 등으로 CareerMate에 저장하세요.
|
|
31
|
+
- **이미지·스캔 PDF**는 텍스트 레이어가 없어 추출되지 않습니다(`unsupported`로 반환되며 `warnings`에 안내). 이때는 이미지를 직접 볼 수 있는 클라이언트(예: Claude)로 읽거나, 사용자에게 내용을 텍스트로 붙여넣어 달라고 안내하세요. 구형 `.doc`/`.ppt`/`.xls`·`.odt`·iWork는 미지원이니 PDF/`.docx`로 다시 저장하도록 안내합니다.
|
|
32
|
+
- 파일은 **로컬에서만** 처리되며 외부로 전송되지 않습니다. 25MB를 넘는 파일은 거부합니다.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 0-1. 인입 폴더 (Inbox)
|
|
37
|
+
|
|
38
|
+
CLI 클라이언트(Claude Code·Codex 등)는 파일 첨부가 안 되므로, 사용자가 기존 이력서·경력기술서 파일을 끌어다 넣을 수 있는 로컬 인입 폴더(`~/.careermate/inbox`)를 두고 그 안의 파일을 읽습니다. 사용자가 파일 **경로**를 직접 알려주는 경우에는 인입 폴더 대신 `read_document`를 쓰세요.
|
|
39
|
+
|
|
40
|
+
| 도구 | 목적 | 주요 입력 | 반환 | 언제 호출 | 읽기전용 |
|
|
41
|
+
| --- | --- | --- | --- | --- | --- |
|
|
42
|
+
| `open_inbox` | 인입 폴더(`~/.careermate/inbox`)를 보장하고 파일 탐색기로 열기 + 현재 들어 있는 파일 목록 반환 | 없음 | `{ dir, files[] }` | 온보딩에서 사용자가 "기존에 작성해 둔 이력서 파일이 있다"고 답했을 때만(묻지 않고 자동으로 열지 마세요) | ✏️ |
|
|
43
|
+
| `read_inbox` | 인입 폴더에 넣어 둔 문서들의 본문을 한 번에 추출해 반환 | `filename?`(대소문자 무시, 생략 시 전체), `max_chars?`(파일별 기본 20000자) | `{ dir, files[] }`(각 `{ filename, path, format, unsupported, truncated, warnings[], text }`) | `open_inbox`로 안내한 뒤 사용자가 "다 넣었어"라고 할 때 | ✅ |
|
|
44
|
+
|
|
45
|
+
- `open_inbox`는 폴더를 열기만 합니다. 사용자가 파일을 다 넣었다고 하면 `read_inbox`로 본문을 읽으세요.
|
|
46
|
+
- `docx`/`hwp`/`hwpx`/텍스트는 텍스트를 추출하고, **`pdf`·이미지는 추출하지 않고 파일 경로만** 돌려주니(`unsupported`) AI 클라이언트의 파일 읽기 기능으로 그 경로를 직접 열어 읽으세요.
|
|
47
|
+
- 읽은 내용은 그대로 새 파일로 다시 쓰지 말고, 구조화해 `save_profile`·`add_resume` 등 알맞은 도구로 CareerMate에 저장하세요.
|
|
48
|
+
- 응답 폭발을 막기 위해 파일별 본문은 `max_chars`(기본 20000자)로 잘라서 돌려주며, 잘린 경우 `truncated: true`로 표시합니다.
|
|
20
49
|
|
|
21
50
|
---
|
|
22
51
|
|
|
@@ -25,9 +54,8 @@ CareerMate는 **MCP-우선** 로컬 커리어 관리 도구입니다. 사용자
|
|
|
25
54
|
| 도구 | 목적 | 주요 입력 | 반환 | 언제 호출 | 읽기전용 |
|
|
26
55
|
| --- | --- | --- | --- | --- | --- |
|
|
27
56
|
| `get_onboarding_status` | CareerMate에 어떤 정보(프로필·이력서·경력·스킬·자소서·공고)가 준비되어 있는지 확인하고 다음 단계를 제시 | 없음 | `completed`, `has_profile`/`has_resume`/`has_cover_letter`/`has_experience`/`has_skills`/`has_job`, `profile_completeness`(0~100), `next_steps[]` | AI 클라이언트 연결 직후 가장 먼저 | ✅ |
|
|
28
|
-
| `start_onboarding` | 가이드형 온보딩 시작. 현재 상태 + AI가 사용자를 안내하는 단계별 지침 반환 | 없음 | `status`(온보딩 상태), `guide`(온보딩 프롬프트 텍스트) | 사용자가 "시작하자 / 설정 도와줘"라고 할 때 | ✅ |
|
|
29
57
|
|
|
30
|
-
|
|
58
|
+
AI 클라이언트가 연결되면 이 도구를 가장 먼저 호출해 상태를 파악한 뒤, 단계별 가이드가 필요하면 `get_workflow_guide('onboarding')`로 받으세요. 안내 흐름: 프로필 입력 → 이력서/경력·스킬 추가 → 대시보드 열기.
|
|
31
59
|
|
|
32
60
|
---
|
|
33
61
|
|
|
@@ -37,7 +65,6 @@ CareerMate는 **MCP-우선** 로컬 커리어 관리 도구입니다. 사용자
|
|
|
37
65
|
| --- | --- | --- | --- | --- | --- |
|
|
38
66
|
| `get_profile` | 기본 프로필 조회(이름, 한 줄 소개, 요약, 희망 직무/조건, 선호 문체, 강조 포인트, 링크) | 없음 | `ProfileRecord` 또는 `null` | 프로필 확인이 필요할 때 | ✅ |
|
|
39
67
|
| `save_profile` | 프로필 저장(부분 저장 안전 — 전달한 필드만 갱신, 나머지는 유지) | 아래 프로필 필드 | 저장된 `ProfileRecord` | 이력서 구조화 후 이름/소개/희망 직무/선호 문체/강조 포인트를 채워 저장할 때 | ✏️ |
|
|
40
|
-
| `update_profile` | 기존 프로필을 유지하며 일부 필드만 수정(`save_profile`과 동일 동작, 의미 명확화용) | 프로필 필드(모두 선택) | 저장된 `ProfileRecord` | 한두 개 항목만 바꿀 때 | ✏️ |
|
|
41
68
|
|
|
42
69
|
프로필 입력 필드(`ProfileInputSchema`):
|
|
43
70
|
|
|
@@ -87,7 +114,6 @@ CareerMate는 **MCP-우선** 로컬 커리어 관리 도구입니다. 사용자
|
|
|
87
114
|
|
|
88
115
|
| 도구 | 목적 | 주요 입력 | 반환 | 언제 호출 | 읽기전용 |
|
|
89
116
|
| --- | --- | --- | --- | --- | --- |
|
|
90
|
-
| `add_cover_letter` | 기존 자기소개서를 등록(`content`를 주면 첫 버전 v1로 저장) | `title`, `content?`, `job_id?`, `is_primary?`, `note?`, `source?` | 생성/저장된 `CoverLetterRecord` | 사용자가 이미 쓴 자소서를 학습용으로 보관할 때 | ✏️ |
|
|
91
117
|
| `get_cover_letters` | 저장된 자기소개서 조회(공고별 필터 가능). 각 항목은 현재 버전 내용·버전 수 포함 | `job_id?` | `CoverLetterRecord[]` | 자소서 목록/내용을 확인할 때 | ✅ |
|
|
92
118
|
| `save_cover_letter_version` | 자기소개서의 새 버전 저장 — 자소서 작성 워크플로우의 핵심 저장 단계 | `content`(필수), `cover_letter_id?`, `title?`, `job_id?`, `note?`, `source?`, `set_current?` | `{ coverLetter, version }` | 새 공고용 자소서를 생성·갱신해 버전으로 남길 때 | ✏️ |
|
|
93
119
|
|
|
@@ -98,7 +124,7 @@ CareerMate는 **MCP-우선** 로컬 커리어 관리 도구입니다. 사용자
|
|
|
98
124
|
- `job_id`로 공고에 연결하면 지원(application) 항목과 자동 연결됩니다.
|
|
99
125
|
- `set_current`(기본 `true`): 이 버전을 현재 버전으로 지정.
|
|
100
126
|
|
|
101
|
-
> 새 공고용 자소서를
|
|
127
|
+
> 기존 자소서를 학습용으로 보관하든 새 공고용 자소서를 새로 쓰든, 저장은 모두 `save_cover_letter_version`으로 합니다. `title`+`content`만 주면 새 자소서의 v1로 저장됩니다.
|
|
102
128
|
|
|
103
129
|
---
|
|
104
130
|
|
|
@@ -109,11 +135,10 @@ CareerMate는 **MCP-우선** 로컬 커리어 관리 도구입니다. 사용자
|
|
|
109
135
|
| `save_job_posting` | 채용공고 저장(있으면 갱신). 저장 시 지원(application) 항목 자동 생성 | `company`, `position`(필수), `url?`, `location?`, `employment_type?`, `description?`, `requirements?`, `keywords?`, `deadline?`, `source?` | 저장된 `JobRecord`(`job_id` 포함) | 공고를 DB에 등록/갱신할 때 | ✏️ |
|
|
110
136
|
| `get_job_posting` | 공고 1건 상세 조회 | `job_id` | `{ job, fit, application, cover_letters, interview }` | 한 공고의 분석·상태·자소서·면접 자료를 한 번에 볼 때 | ✅ |
|
|
111
137
|
| `list_jobs` | 저장된 모든 공고를 지원 상태·적합도 점수와 함께 목록화 | 없음 | 공고 메타 포함 배열 | 전체 공고를 훑어볼 때 | ✅ |
|
|
112
|
-
| `parse_job_posting` | 붙여넣은 공고 원문을 정리해 회사명/직무/마감일 추정 + 기술 키워드 추출 | `raw`(공고 원문 텍스트) | 정리된 공고 구조 | `save_job_posting` 전에 빠르게 구조화할 때(보조) | ✅ |
|
|
113
138
|
|
|
114
139
|
- `url`이 같으면 중복 생성 없이 갱신됩니다.
|
|
115
140
|
- `requirements`에는 자격요건/우대사항 핵심, `keywords`에는 핵심 키워드를 정리해 넣으면 이후 적합도 분석·자소서 작성에 활용됩니다.
|
|
116
|
-
-
|
|
141
|
+
- 붙여넣은 공고 원문의 구조화(회사명/직무/마감일/키워드 추출)는 AI가 직접 수행한 뒤 `save_job_posting`으로 저장합니다.
|
|
117
142
|
|
|
118
143
|
---
|
|
119
144
|
|
|
@@ -217,6 +242,21 @@ CareerMate는 **MCP-우선** 로컬 커리어 관리 도구입니다. 사용자
|
|
|
217
242
|
|
|
218
243
|
---
|
|
219
244
|
|
|
245
|
+
## 14. 업데이트 (Update)
|
|
246
|
+
|
|
247
|
+
CareerMate를 최신 버전으로 유지하기 위한 유지보수 도구입니다. 확인은 `check_for_update`로, 실제 적용은 `update_careermate`로 합니다.
|
|
248
|
+
|
|
249
|
+
| 도구 | 목적 | 주요 입력 | 반환 | 언제 호출 | 읽기전용 |
|
|
250
|
+
| --- | --- | --- | --- | --- | --- |
|
|
251
|
+
| `check_for_update` | npm 레지스트리에서 새 버전이 있는지 지금 확인(공개 버전 정보만 GET, 사용자 데이터 미전송) | 없음 | `{ current, latest, update_available }` | 사용자가 "최신이야?/업데이트 있어?"라고 묻거나 `get_onboarding_status`가 새 버전을 알릴 때 | ✅ |
|
|
252
|
+
| `update_careermate` | 설치 폴더에 `careermate@latest`를 npm으로 설치해 최신 버전으로 업데이트 | 없음 | 업데이트 결과(`{ ok, message, manual? }`) | 사용자가 명시적으로 "업데이트해줘"라고 할 때만 | ✏️ |
|
|
253
|
+
|
|
254
|
+
- `check_for_update`는 확인만 하며 데이터를 변경하지 않습니다. 새 버전이 있으면 `update_careermate`로 적용하세요.
|
|
255
|
+
- `update_careermate` 성공 후에는 적용을 위해 AI 앱(또는 MCP 연결) **재시작**이 필요합니다. 재시작 시 데이터베이스는 자동 마이그레이션되어 기존 데이터가 보존됩니다.
|
|
256
|
+
- 자동 설치가 막히면(개발 모드·비표준 설치) 직접 칠 명령(`manual`)을 안내합니다.
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
220
260
|
## ⭐ get_application_context — 분석/작성 전 필수 호출
|
|
221
261
|
|
|
222
262
|
> `get_application_context`는 **가장 중요한 도구**입니다. 공고를 분석하거나 자기소개서를 작성하기 **전에 반드시 먼저** 호출하세요. 한 번의 호출로 사용자에 대한 모든 맥락을 돌려줍니다. 읽기전용(✅)입니다.
|
package/docs/ROADMAP.md
CHANGED
|
@@ -42,10 +42,11 @@ CareerMate는 MCP-우선 로컬 커리어 관리 도구다. 사용자는 자신
|
|
|
42
42
|
|
|
43
43
|
### MCP 서버 (`apps/mcp`) + 도구 (`packages/mcp-tools`)
|
|
44
44
|
- [x] **MCP stdio 서버**: `@modelcontextprotocol/sdk` 기반, AI 클라이언트가 `npm run mcp`로 기동, 대시보드와 동일 DB 공유
|
|
45
|
-
- [x]
|
|
46
|
-
- 온보딩/프로필: `get_onboarding_status`, `
|
|
47
|
-
- 문서: `add_resume`, `get_resumes`, `
|
|
48
|
-
-
|
|
45
|
+
- [x] **커리어 워크플로 MCP 도구 (26개)**:
|
|
46
|
+
- 온보딩/프로필: `get_onboarding_status`, `save_profile`, `get_profile`
|
|
47
|
+
- 문서: `add_resume`, `get_resumes`, `get_cover_letters`
|
|
48
|
+
- 경력·프로젝트·스킬: `add_experience`, `get_experiences`, `add_project`, `get_projects`, `add_skill`, `get_skills`
|
|
49
|
+
- 공고: `save_job_posting`, `get_job_posting`, `list_jobs`
|
|
49
50
|
- 컨텍스트(핵심): `get_application_context`
|
|
50
51
|
- 분석/작성: `save_fit_analysis`, `save_cover_letter_version`
|
|
51
52
|
- 지원/면접: `update_application_status`, `save_interview_prep`
|
|
@@ -57,7 +58,7 @@ CareerMate는 MCP-우선 로컬 커리어 관리 도구다. 사용자는 자신
|
|
|
57
58
|
|
|
58
59
|
### 기능 흐름 (저장/관리)
|
|
59
60
|
- [x] **온보딩**: 프로필·경력·프로젝트·스킬 입력 흐름
|
|
60
|
-
- [x] **공고
|
|
61
|
+
- [x] **공고 저장**: 공고 본문 저장(회사명/직무/키워드 구조화는 AI가 수행)
|
|
61
62
|
- [x] **적합도(fit) 분석 저장**: `save_fit_analysis`
|
|
62
63
|
- [x] **자소서 버전 관리**: `cover_letter_versions` 테이블 + 타임라인 (Documents 페이지)
|
|
63
64
|
- [x] **지원 상태 관리**: 8단계 칸반 (Applications 페이지) + `update_application_status`
|
|
@@ -92,8 +93,8 @@ CareerMate는 MCP-우선 로컬 커리어 관리 도구다. 사용자는 자신
|
|
|
92
93
|
아래 항목은 **지금은 제외하되, 구조는 열어둔다**. 즉 현재 MVP 범위에서 구현하지 않지만, 패키지 경계·데이터 모델·도구 인터페이스가 이후 추가를 수용할 수 있도록 설계되어 있다.
|
|
93
94
|
|
|
94
95
|
### 공고 크롤링 (사람인 / 잡코리아 / 원티드)
|
|
95
|
-
- 현재는 사용자가 공고 본문을 붙여넣으면 `
|
|
96
|
-
- 구조 개방:
|
|
96
|
+
- 현재는 사용자가 공고 본문을 붙여넣으면 AI가 직접 구조화해 `save_job_posting`으로 저장한다. URL 입력 시 자동 크롤링은 미구현.
|
|
97
|
+
- 구조 개방: 텍스트 정제 로직이 `packages/parsers`로 분리되어 있어(대시보드 `/api/parse/job`에서 사용), 사이트별 크롤러/어댑터를 추가하는 형태로 확장 가능.
|
|
97
98
|
- **지금은 제외, 구조는 열어둠.**
|
|
98
99
|
|
|
99
100
|
### 클라우드 동기화
|
package/docs/START_WORKFLOW.md
CHANGED
|
@@ -15,14 +15,14 @@
|
|
|
15
15
|
- 연결 직후 가장 먼저 호출합니다.
|
|
16
16
|
- 결과의 `has_profile` / `has_resume` / `has_cover_letter` / `has_experience` / `has_skills` / `has_job`, `profile_completeness`(0~100), `next_steps`를 확인합니다.
|
|
17
17
|
- 사용자에게 "지금까지 등록된 정보"와 "앞으로 채우면 좋은 정보"를 짧게 안내합니다. **이미 채워진 항목은 다시 묻지 마세요.**
|
|
18
|
-
- (선택)
|
|
18
|
+
- (선택) 단계별 가이드가 필요하면 `get_workflow_guide('onboarding')`로 받을 수 있습니다.
|
|
19
19
|
|
|
20
20
|
### 2. 프로필 수집 및 저장 — `save_profile`
|
|
21
21
|
|
|
22
22
|
- 사용자가 올린 이력서/자기소개서 파일 또는 직접 알려준 내용을 바탕으로 프로필을 구조화합니다.
|
|
23
23
|
- 정리 항목: 이름, 연락처, 한 줄 소개(`headline`), 요약(`summary`), 희망 직무(`desired_roles`), 희망 근무 조건(`desired_conditions`).
|
|
24
24
|
- **글쓰기 선호도**를 함께 물어보세요: 선호 문체(`preferred_tone`, 예: "담백하고 구체적")와 강조 포인트(`emphasis_points`). 이 두 값은 이후 자기소개서 품질에 직접 영향을 줍니다.
|
|
25
|
-
- `save_profile`로 저장합니다. 전달한 필드만 갱신되고 나머지는 유지됩니다(부분 저장 안전). 한두 개만 바꿀
|
|
25
|
+
- `save_profile`로 저장합니다. 전달한 필드만 갱신되고 나머지는 유지됩니다(부분 저장 안전). 한두 개만 바꿀 때도 그대로 `save_profile`을 다시 호출하면 됩니다.
|
|
26
26
|
- 파일에서 읽은 정보는 `source`를 `upload`, 사용자가 말로 준 정보는 `manual`로 표시합니다.
|
|
27
27
|
|
|
28
28
|
### 3. 이력서 등록 — `add_resume`
|
|
@@ -35,10 +35,16 @@
|
|
|
35
35
|
- `kind`(resume / career_description / portfolio / other)와 `title`을 적절히 지정하고, 대표 문서는 `is_primary=true`로 설정합니다. 대표 문서는 이후 `get_application_context`의 `primary_resume`로 노출됩니다.
|
|
36
36
|
- `source`는 `upload`를 사용합니다.
|
|
37
37
|
|
|
38
|
-
###
|
|
38
|
+
### 3-1. 경력·프로젝트·스킬 등록 (있는 경우) — `add_experience` · `add_project` · `add_skill`
|
|
39
39
|
|
|
40
|
-
-
|
|
41
|
-
- `
|
|
40
|
+
- 이력서에서 파악되는 경력·프로젝트·보유 기술은 본문 저장과 별개로 `add_experience` / `add_project` / `add_skill`로 항목 단위 저장하면 적합도 분석·자소서·면접 준비에서 정밀하게 활용됩니다.
|
|
41
|
+
- 경력 `achievements`에는 가능하면 정량 지표를 넣고, 기술은 항목당 1건이라 여러 개면 `add_skill`을 반복 호출합니다.
|
|
42
|
+
- 이 항목들은 `get_onboarding_status`의 `has_experience` / `has_skills`와 프로필 완성도(0~100)에 반영됩니다.
|
|
43
|
+
|
|
44
|
+
### 4. 자기소개서 등록 (있는 경우) — `save_cover_letter_version`
|
|
45
|
+
|
|
46
|
+
- 기존 자기소개서가 있으면 `save_cover_letter_version`으로 저장합니다(`title` + `content`). 특정 공고용이면 `job_id`를 연결합니다.
|
|
47
|
+
- `cover_letter_id` 없이 `title`+`content`만 주면 새 자소서의 첫 버전(v1)으로 저장됩니다. 학습/참고용 보관에 적합합니다.
|
|
42
48
|
- 없으면 "나중에 공고를 분석한 뒤 맞춤 자기소개서를 써드릴 수 있어요"라고 안내만 합니다.
|
|
43
49
|
|
|
44
50
|
### 5. 대시보드 열기 & 다음 단계 — `open_dashboard`
|
|
@@ -56,7 +62,7 @@
|
|
|
56
62
|
### 1. 공고 저장 — `save_job_posting`
|
|
57
63
|
|
|
58
64
|
- `company`, `position`은 필수입니다. 공고 원문은 `description`에, 핵심 자격/우대는 `requirements` 배열에, 키워드는 `keywords`에 정리해 넣습니다.
|
|
59
|
-
-
|
|
65
|
+
- 붙여넣은 원문의 회사명/직무/마감일/기술 키워드 구조화는 AI가 직접 수행한 뒤 위 필드에 채워 저장하세요.
|
|
60
66
|
- 저장하면 지원(application) 항목이 자동 생성되고 `job_id`가 반환됩니다. 같은 `url`이면 중복 없이 갱신됩니다.
|
|
61
67
|
|
|
62
68
|
### 2. 지원 맥락 가져오기 — `get_application_context` ⭐ (반드시 먼저)
|
|
@@ -96,7 +102,7 @@
|
|
|
96
102
|
|
|
97
103
|
### `update_application_status`
|
|
98
104
|
|
|
99
|
-
- 지원 상태를 변경합니다. 가능한
|
|
105
|
+
- 지원 상태를 변경합니다. 가능한 상태는 8단계뿐입니다(AGENTS.md 기준): draft(작성 중), planned(지원 예정), applied(지원 완료), document_passed(서류 합격), interview(면접 진행), final_passed(최종 합격), rejected(불합격), on_hold(보류). 이 외의 값(saved·offer·closed 등)은 대시보드가 모르므로 쓰지 마세요.
|
|
100
106
|
- `note`에 변경 사유를 남길 수 있습니다.
|
|
101
107
|
- 상태가 **서류 합격(document_passed) 이상**으로 바뀌면 면접 준비를 제안하는 힌트를 함께 돌려줍니다 → 다음 단계(E)로 이어집니다.
|
|
102
108
|
|
package/docs/TODO.md
CHANGED
|
@@ -54,4 +54,4 @@
|
|
|
54
54
|
- **.mcpb GitHub Release 생성됨(접근 제한)** — Release `v0.1.0`에 `careermate.mcpb` 첨부. 단 저장소가 **비공개**라 공개 다운로드 링크는 아직 없음. → 저장소를 public으로 전환하면 공개 다운로드 활성화.
|
|
55
55
|
- **공개 repo URL 확정** — git remote 기준 정본은 `osntak/CareerMate`(현재 **비공개**). 공개 전환 여부는 별도 결정 사항.
|
|
56
56
|
- **THIRD_PARTY_NOTICES — `im-not-ai`(MIT)** 항목의 저작권자/연도가 비어 있다. 실제 저작권자·연도로 채우기.
|
|
57
|
-
-
|
|
57
|
+
- **`install-page/` 폴더명 변경** — 이제 랜딩+시작마법사+데모(`/demo`)+설치안내를 모두 담는 "공개 웹사이트" 폴더라 `install-page`는 이름이 안 맞는다. 후보: `site` / `www` / `landing`. 주의: 이 폴더는 **Vercel 프로젝트의 Root Directory 설정**이므로, 폴더명을 바꾸면 Vercel 대시보드의 Root Directory 값도 같이 바꿔야 배포가 안 깨진다. 레포 내 코드 참조(고쳐야 할 곳): `scripts/build-dist.mjs`·`scripts/build-mcpb.mjs`·`scripts/shots.ts`·`scripts/gen-og.mjs`·`apps/web/src/server.ts`. 데모 완성 후 진행하기로 합의(2026-06-16).
|
package/docs/WORKFLOWS.md
CHANGED
|
@@ -8,7 +8,7 @@ CareerMate는 내부에 LLM을 두지 않는다. 분석과 작성은 사용자
|
|
|
8
8
|
|
|
9
9
|
- 연결 직후 AI가 따라가는 시작 런북은 [시작 런북](./START_WORKFLOW.md)을 참고한다.
|
|
10
10
|
- 설치·연결 절차는 [설치 런북](../INSTALL.md)(또는 [지원 앱·설치 레퍼런스](./SUPPORTED_AI_APPS.md))을 참고한다.
|
|
11
|
-
- MCP 도구
|
|
11
|
+
- MCP 도구 31종의 입출력 명세는 코드(`packages/mcp-tools/src/tools.ts`)에 정의되어 있으며, AI는 각 도구의 `inputSchema`로 직접 확인할 수 있다.
|
|
12
12
|
|
|
13
13
|
## 워크플로우 5종 개요
|
|
14
14
|
|
|
@@ -41,9 +41,10 @@ CareerMate는 내부에 LLM을 두지 않는다. 분석과 작성은 사용자
|
|
|
41
41
|
2. 비어 있는 항목을 사용자에게 쉬운 한국어로 안내하고, 이력서/자기소개서 파일 업로드 또는 정보 입력을 요청한다.
|
|
42
42
|
3. 수집한 정보를 구조화해 `save_profile`로 저장한다(이름·연락처·`headline`·`summary`·`desired_roles`·`desired_conditions`, 그리고 글쓰기 선호인 `preferred_tone`과 `emphasis_points` 포함). 파일 출처는 `source=upload`, 직접 입력은 `manual`.
|
|
43
43
|
4. 업로드한 이력서/경력기술서/포트폴리오 본문을 `add_resume`로 저장한다(`kind`·`title` 지정, 대표 문서는 `is_primary=true`).
|
|
44
|
-
5.
|
|
45
|
-
6. `
|
|
46
|
-
7.
|
|
44
|
+
5. 경력·프로젝트·스킬이 파악되면 `add_experience`·`add_project`·`add_skill`로 각각 저장한다(완성도의 `has_experience`/`has_skills`에 반영).
|
|
45
|
+
6. 기존 자기소개서가 있으면 `save_cover_letter_version`으로 저장한다(`title`+`content`면 v1로 저장, 없으면 나중에 작성 가능함을 안내).
|
|
46
|
+
7. `open_dashboard`를 호출해 사용자가 자기 데이터를 확인하게 한다.
|
|
47
|
+
8. 다시 `get_onboarding_status`로 완성도를 확인하고, 다음 단계(공고 분석 등)를 제안한다.
|
|
47
48
|
|
|
48
49
|
**도구 호출 순서 요약:**
|
|
49
50
|
|
|
@@ -51,7 +52,8 @@ CareerMate는 내부에 LLM을 두지 않는다. 분석과 작성은 사용자
|
|
|
51
52
|
get_onboarding_status
|
|
52
53
|
→ save_profile
|
|
53
54
|
→ add_resume
|
|
54
|
-
→
|
|
55
|
+
→ add_experience / add_project / add_skill (있으면)
|
|
56
|
+
→ save_cover_letter_version (기존 자소서가 있으면)
|
|
55
57
|
→ open_dashboard
|
|
56
58
|
→ get_onboarding_status (재확인)
|
|
57
59
|
```
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# 데모 대시보드 배포 설계 (careermate.life/demo)
|
|
2
|
+
|
|
3
|
+
작성일: 2026-06-16
|
|
4
|
+
|
|
5
|
+
## 목표
|
|
6
|
+
|
|
7
|
+
랜딩 방문자가 **로그인·설치 없이** CareerMate 대시보드를 둘러볼 수 있는 데모 페이지를
|
|
8
|
+
`careermate.life/demo`에 배포한다. 사용자(개발자)가 원격에서 로컬 대시보드(127.0.0.1)에
|
|
9
|
+
접근할 수 없는 문제도 함께 해소한다.
|
|
10
|
+
|
|
11
|
+
## 결정 사항 (사용자 확인 완료)
|
|
12
|
+
|
|
13
|
+
- **상호작용**: 편집 가능. 단 메모리 전용 — 디스크/서버 저장 없음, 새로고침 시 샘플로 리셋.
|
|
14
|
+
- **위치**: 기존 Vercel 랜딩(`install-page/`) 하위 `/demo` 서브경로. Vercel 설정·빌드 무변경.
|
|
15
|
+
|
|
16
|
+
## 핵심 구조
|
|
17
|
+
|
|
18
|
+
대시보드는 정적 SPA(`apps/web/public`)가 로컬 Node 서버의 `/api/*` JSON을 호출하는 구조다.
|
|
19
|
+
서버 없이 배포하려면 **클라이언트에서 `fetch('/api/*')`를 가로채 메모리 DB로 응답하는 shim**을 끼운다.
|
|
20
|
+
|
|
21
|
+
대부분의 GET 응답(`/api/summary`, `/api/applications`, `/api/interview`, `/api/jobs/:id` 등)은
|
|
22
|
+
몇 개 기본 컬렉션(jobs, applications, coverLetters, profile, experiences, projects, skills,
|
|
23
|
+
documents, interviews, fits, activities)에서 **파생**된다. shim이 그 파생 로직을 재현하므로
|
|
24
|
+
"한 곳을 수정하면 홈 카운트·목록이 자동 갱신"되는 동작이 공짜로 따라온다.
|
|
25
|
+
|
|
26
|
+
## 구성 요소
|
|
27
|
+
|
|
28
|
+
1. **데모 오버레이 소스** (`apps/web/demo/`, 원본 대시보드와 분리)
|
|
29
|
+
- `seed.js` — 풍부한 샘플 데이터(공고 6+, 지원 단계 다양, 프로필/경력/스킬/문서/자소서/면접).
|
|
30
|
+
- `demo-shim.js` — 메모리 가변 DB + `window.fetch` 가로채기. GET=읽기, POST/PUT/DELETE=메모리 변경.
|
|
31
|
+
서버 라우트(`apps/web/src/routes.ts`)와 동일한 응답 형태/파생 로직을 미러링.
|
|
32
|
+
- `demo.css` + 하단 고정 배너(데모 안내 + "CareerMate 소개로 →" 링크).
|
|
33
|
+
|
|
34
|
+
2. **빌드 스크립트** (`scripts/build-demo.mjs`)
|
|
35
|
+
- `apps/web/public/*` → `install-page/demo/`로 복사.
|
|
36
|
+
- 복사본의 루트 절대경로(`/lib.js`, `/app.js`, `/pages/`, `/styles.css`, `/fonts/`,
|
|
37
|
+
`/icon-180.png`)를 `/demo/...`로 재작성. `/api/`는 그대로(shim이 처리).
|
|
38
|
+
- `index.html` `<head>`에 데모 shim(classic script, fetch 선패치) + `demo.css` 주입,
|
|
39
|
+
`<body>` 끝에 데모 배너 주입.
|
|
40
|
+
- Node 25의 `fs.rmSync(recursive)` 크래시 회피 → 수동 재귀 삭제 사용.
|
|
41
|
+
|
|
42
|
+
3. **진입 동선**
|
|
43
|
+
- 랜딩(`install-page/index.html`)에 "데모 둘러보기" 링크.
|
|
44
|
+
- 데모 하단 배너의 "CareerMate 소개로 →" 버튼이 랜딩(`/`)으로 복귀.
|
|
45
|
+
|
|
46
|
+
4. **배포**
|
|
47
|
+
- 생성물 `install-page/demo/`를 git 커밋 → 기존 Vercel이 자동 서빙.
|
|
48
|
+
- 갱신: `node scripts/build-demo.mjs`.
|
|
49
|
+
|
|
50
|
+
## 후속 작업 (TODO)
|
|
51
|
+
|
|
52
|
+
- [ ] **`install-page/` 폴더명 변경** — 이제 랜딩+시작마법사+데모+설치안내를 모두 담는
|
|
53
|
+
"공개 웹사이트"라 `install-page`는 이름이 안 맞음. 후보: `site` / `www` / `landing`.
|
|
54
|
+
주의: 이 폴더는 Vercel 프로젝트의 **Root Directory 설정**이라, 폴더명을 바꾸면
|
|
55
|
+
Vercel 대시보드의 Root Directory 값도 같이 바꿔야 배포가 안 깨짐(레포 내 참조는 코드 5곳
|
|
56
|
+
+문서: `scripts/build-dist.mjs`·`build-mcpb.mjs`·`shots.ts`·`gen-og.mjs`·`apps/web/src/server.ts`).
|
|
57
|
+
→ **데모 완성 후 진행하기로 사용자와 합의(2026-06-16).** 그때까지는 `install-page/` 유지.
|
|
58
|
+
|
|
59
|
+
## 비고 / 한계
|
|
60
|
+
|
|
61
|
+
- 쓰기 동작 중 AI가 생성하는 것(적합도 분석·자소서 본문·면접 질문)은 시드에 미리 채워 보여주고,
|
|
62
|
+
대시보드의 수동 쓰기 경로(공고/프로필/상태/문서 CRUD)만 메모리에 반영한다.
|
|
63
|
+
- 데이터는 전부 브라우저 메모리에만 존재. 외부 전송 없음(프라이버시 약속 유지).
|
|
64
|
+
- 검증: 로컬에서 `install-page`를 정적 서빙하고 Playwright로 `/demo` 전 페이지 스크린샷 확인.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "careermate",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CareerMate — an MCP-first, local-first career workspace. Connect it to a local AI agent (Claude Desktop, Claude Code, Codex); CareerMate stores your career data locally on your machine and exposes it over MCP.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -56,13 +56,17 @@
|
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
59
|
+
"fflate": "^0.8.3",
|
|
60
|
+
"hwp.js": "^0.0.3",
|
|
61
|
+
"mammoth": "^1.12.0",
|
|
62
|
+
"pdfjs-dist": "^6.0.0",
|
|
59
63
|
"tsx": "^4.19.2",
|
|
60
64
|
"zod": "^3.24.1"
|
|
61
65
|
},
|
|
62
66
|
"devDependencies": {
|
|
63
67
|
"@playwright/test": "^1.60.0",
|
|
64
68
|
"@types/node": "^22.10.0",
|
|
65
|
-
"esbuild": "^0.
|
|
69
|
+
"esbuild": "^0.28.1",
|
|
66
70
|
"playwright": "^1.60.0",
|
|
67
71
|
"typescript": "^5.7.2"
|
|
68
72
|
}
|
package/packages/db/src/paths.ts
CHANGED
|
@@ -34,6 +34,17 @@ export function getUploadsDir(): string {
|
|
|
34
34
|
return dir;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Drop folder where the user places existing résumé/career files for the AI to
|
|
39
|
+
* ingest — a workaround for CLI clients that can't attach files (only paths).
|
|
40
|
+
* Lives under the data dir (not the user's project) so it never pollutes a repo.
|
|
41
|
+
*/
|
|
42
|
+
export function getInboxDir(): string {
|
|
43
|
+
const dir = path.join(getDataDir(), 'inbox');
|
|
44
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
45
|
+
return dir;
|
|
46
|
+
}
|
|
47
|
+
|
|
37
48
|
export function getBackupsDir(): string {
|
|
38
49
|
const dir = path.join(getDataDir(), 'backups');
|
|
39
50
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -5,14 +5,11 @@
|
|
|
5
5
|
* web server publishes it to a runtime file (see @careermate/db runtime). If the
|
|
6
6
|
* server isn't running we start it (detached) and wait for it to register.
|
|
7
7
|
*/
|
|
8
|
-
import { spawn } from 'node:child_process';
|
|
8
|
+
import { spawn, type SpawnOptions } from 'node:child_process';
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import { fileURLToPath } from 'node:url';
|
|
11
11
|
import { readRuntimeInfo, isProcessAlive } from '@careermate/db';
|
|
12
|
-
|
|
13
|
-
// 번들 빌드(esbuild)에서 true로 치환된다. tsx 개발 실행에서는 정의되지 않아 typeof로 안전 분기.
|
|
14
|
-
declare const __BUNDLED__: boolean | undefined;
|
|
15
|
-
const BUNDLED = typeof __BUNDLED__ !== 'undefined' && __BUNDLED__;
|
|
12
|
+
import { BUNDLED } from '@careermate/shared';
|
|
16
13
|
|
|
17
14
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
18
15
|
// packages/mcp-tools/src -> repo root (dev). 번들에서는 __dirname이 dist/ 이다.
|
|
@@ -28,26 +25,38 @@ function liveServer() {
|
|
|
28
25
|
return null;
|
|
29
26
|
}
|
|
30
27
|
|
|
31
|
-
/**
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Fire-and-forget detached spawn that NEVER throws — synchronously OR
|
|
30
|
+
* asynchronously. A missing binary (e.g. `xdg-open` on a headless Linux box)
|
|
31
|
+
* makes spawn emit an async `'error'` event; with no listener that becomes an
|
|
32
|
+
* uncaughtException and crashes the MCP process. A surrounding try/catch only
|
|
33
|
+
* guards the synchronous spawn() call, not the later event — so attach the
|
|
34
|
+
* listener here. stdio defaults to 'ignore'; callers may override via opts.
|
|
35
|
+
*/
|
|
36
|
+
function spawnDetached(cmd: string, args: string[], opts: SpawnOptions = {}): void {
|
|
36
37
|
try {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
: [process.execPath, ['--no-warnings', '--experimental-sqlite', '--import', 'tsx', 'apps/web/src/index.ts'], REPO_ROOT];
|
|
41
|
-
const child = spawn(bin, args, {
|
|
42
|
-
cwd,
|
|
43
|
-
detached: true,
|
|
44
|
-
stdio: 'ignore',
|
|
45
|
-
env: { ...process.env, CAREERMATE_NO_OPEN: '1' },
|
|
38
|
+
const child = spawn(cmd, args, { stdio: 'ignore', detached: true, ...opts });
|
|
39
|
+
child.on('error', () => {
|
|
40
|
+
/* binary missing / spawn failed — best-effort, never crash */
|
|
46
41
|
});
|
|
47
42
|
child.unref();
|
|
48
43
|
} catch {
|
|
49
|
-
|
|
44
|
+
/* best-effort */
|
|
50
45
|
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Start the dashboard server detached and wait until it publishes its address. */
|
|
49
|
+
export async function ensureServer(timeoutMs = 12000): Promise<{ url: string } | null> {
|
|
50
|
+
const existing = liveServer();
|
|
51
|
+
if (existing) return { url: existing.url };
|
|
52
|
+
|
|
53
|
+
// prod(번들): dist/web.mjs를 플레인 node로 실행. dev: tsx로 소스 실행.
|
|
54
|
+
const [bin, args, cwd] = BUNDLED
|
|
55
|
+
? [process.execPath, ['--experimental-sqlite', path.join(__dirname, 'web.mjs')], __dirname]
|
|
56
|
+
: [process.execPath, ['--no-warnings', '--experimental-sqlite', '--import', 'tsx', 'apps/web/src/index.ts'], REPO_ROOT];
|
|
57
|
+
// Spawn failures (sync or async) just leave the server down; the poll loop
|
|
58
|
+
// below then times out and returns null — no crash.
|
|
59
|
+
spawnDetached(bin as string, args as string[], { cwd: cwd as string, env: { ...process.env, CAREERMATE_NO_OPEN: '1' } });
|
|
51
60
|
|
|
52
61
|
const start = Date.now();
|
|
53
62
|
while (Date.now() - start < timeoutMs) {
|
|
@@ -58,21 +67,35 @@ export async function ensureServer(timeoutMs = 12000): Promise<{ url: string } |
|
|
|
58
67
|
return null;
|
|
59
68
|
}
|
|
60
69
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
/** Open a URL or a local folder via the OS default handler. Best-effort, never throws. */
|
|
71
|
+
function openOsTarget(target: string): void {
|
|
72
|
+
switch (process.platform) {
|
|
73
|
+
case 'win32':
|
|
74
|
+
// `start` is a cmd builtin; the empty "" is the window-title arg so a
|
|
75
|
+
// quoted target isn't mistaken for the title. (explorer also opens URLs,
|
|
76
|
+
// but `start` is the canonical default-handler launcher.)
|
|
77
|
+
spawnDetached('cmd', ['/c', 'start', '', target]);
|
|
78
|
+
break;
|
|
79
|
+
case 'darwin':
|
|
80
|
+
spawnDetached('open', [target]);
|
|
81
|
+
break;
|
|
82
|
+
default:
|
|
83
|
+
spawnDetached('xdg-open', [target]);
|
|
73
84
|
}
|
|
74
85
|
}
|
|
75
86
|
|
|
87
|
+
export function openInBrowser(url: string): void {
|
|
88
|
+
openOsTarget(url);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Open a local folder in the OS file manager (Explorer/Finder/xdg). Best-effort. */
|
|
92
|
+
export function openInFileManager(dir: string): void {
|
|
93
|
+
// Windows: `explorer <dir>` is the idiomatic folder opener (start "" <dir>
|
|
94
|
+
// also works, but explorer focuses an existing window if already open).
|
|
95
|
+
if (process.platform === 'win32') spawnDetached('explorer', [dir]);
|
|
96
|
+
else openOsTarget(dir);
|
|
97
|
+
}
|
|
98
|
+
|
|
76
99
|
/** Resolve the dashboard URL, starting the server if needed. */
|
|
77
100
|
export async function resolveDashboardUrl(hash = ''): Promise<{ url: string; running: boolean }> {
|
|
78
101
|
const live = liveServer();
|
|
@@ -6,3 +6,12 @@
|
|
|
6
6
|
export { TOOLS, EXPORTS_LOCATION } from './tools.ts';
|
|
7
7
|
export { ok, fail, toCallToolResult, type ToolDef, type ToolResult } from './result.ts';
|
|
8
8
|
export { resolveDashboardUrl, openInBrowser, ensureServer, projectRoot } from './bridge.ts';
|
|
9
|
+
export {
|
|
10
|
+
getUpdateStatus,
|
|
11
|
+
getUpdateStatusAsync,
|
|
12
|
+
runSelfUpdate,
|
|
13
|
+
installPrefix,
|
|
14
|
+
compareVersions,
|
|
15
|
+
type UpdateStatus,
|
|
16
|
+
type UpdateResult,
|
|
17
|
+
} from './update.ts';
|