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
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# CareerMate 아키텍처
|
|
2
|
+
|
|
3
|
+
이 문서는 CareerMate의 전체 구조를 설명한다. CareerMate는 **MCP-우선 로컬 커리어 관리 도구**다. 사용자는 자신의 AI(ChatGPT/Claude/Gemini 등)와 대화하고, 그 AI가 CareerMate의 로컬 MCP 도구를 호출해 로컬 커리어 DB를 읽고 쓴다. CareerMate 내부에는 LLM이 없다 — 분석·작성 같은 추론은 전적으로 사용자의 AI가 수행하고, CareerMate는 데이터를 제공·보관하는 역할만 한다. 모든 데이터는 사용자의 로컬 머신에만 저장된다.
|
|
4
|
+
|
|
5
|
+
기술 선택의 배경(무빌드·무네이티브 등)과 보안 모델의 세부는 [DECISIONS.md](./DECISIONS.md)(특히 "로컬-우선 보안 모델" 절)를 참고하라. 데이터 모델은 [DATA_MODEL.md](./DATA_MODEL.md), MCP 도구 목록은 [MCP_TOOLS.md](./MCP_TOOLS.md)를 함께 보면 좋다.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. 큰 그림: 두 프로세스, 하나의 SQLite
|
|
10
|
+
|
|
11
|
+
CareerMate는 독립적으로 동작하는 **두 개의 로컬 프로세스**로 구성된다.
|
|
12
|
+
|
|
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 프로토콜로 통신하며, 24개의 CareerMate 도구를 노출한다.
|
|
15
|
+
|
|
16
|
+
두 프로세스는 서로를 직접 호출하지 않는다. 대신 **같은 로컬 SQLite 파일**(`~/.careermate/careermate.sqlite`)을 공유한다. AI가 도구로 쓴 내용은 대시보드에 즉시 보이고, 사용자가 대시보드에서 편집한 내용은 AI가 다음 도구 호출에서 그대로 읽는다. SQLite는 WAL(Write-Ahead Logging) 모드로 열려 두 프로세스의 동시 읽기/쓰기를 안전하게 처리한다.
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
사용자의 머신 (모든 것이 로컬)
|
|
20
|
+
┌───────────────────────────────────────────────────────────────────────────┐
|
|
21
|
+
│ │
|
|
22
|
+
│ 사람(브라우저) 사용자의 AI 클라이언트 │
|
|
23
|
+
│ │ (Claude / ChatGPT / Gemini / Cursor)│
|
|
24
|
+
│ │ HTTP (127.0.0.1) │ MCP over stdio │
|
|
25
|
+
│ ▼ ▼ │
|
|
26
|
+
│ ┌──────────────────────────┐ ┌──────────────────────────┐ │
|
|
27
|
+
│ │ apps/web │ │ apps/mcp │ │
|
|
28
|
+
│ │ 대시보드 웹서버 │ │ MCP stdio 서버 │ │
|
|
29
|
+
│ │ (npm start) │ │ (npm run mcp, │ │
|
|
30
|
+
│ │ │ │ AI 클라이언트가 기동) │ │
|
|
31
|
+
│ │ 보안 게이트 → API 라우터 │ │ 24개 도구 등록 │ │
|
|
32
|
+
│ │ → 정적 대시보드/설치페이지│ │ toCallToolResult │ │
|
|
33
|
+
│ └───────────┬──────────────┘ └───────────┬──────────────┘ │
|
|
34
|
+
│ │ │ │
|
|
35
|
+
│ │ getDb() │ getDb() │
|
|
36
|
+
│ ▼ ▼ │
|
|
37
|
+
│ ┌─────────────────────────────────────────────────┐ │
|
|
38
|
+
│ │ packages/db (단일 DatabaseSync 연결) │ │
|
|
39
|
+
│ │ node:sqlite + WAL + foreign_keys + busy_timeout│ │
|
|
40
|
+
│ └───────────────────────┬─────────────────────────┘ │
|
|
41
|
+
│ │ │
|
|
42
|
+
│ ▼ │
|
|
43
|
+
│ ┌─────────────────────────────────────────────────┐ │
|
|
44
|
+
│ │ ~/.careermate/ │ │
|
|
45
|
+
│ │ careermate.sqlite ← 공유 DB(12개 테이블) │ │
|
|
46
|
+
│ │ server.json ← 런타임 핸드셰이크 │ │
|
|
47
|
+
│ │ exports/ backups/ uploads/ │ │
|
|
48
|
+
│ └─────────────────────────────────────────────────┘ │
|
|
49
|
+
│ │
|
|
50
|
+
└───────────────────────────────────────────────────────────────────────────┘
|
|
51
|
+
└────────────── 네트워크로 나가는 호출 없음 · LLM 내장 없음 ────────────┘
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
두 프로세스가 동시에 기동되어 있을 필요는 없다. MCP 서버만 떠 있어도 AI가 DB를 읽고 쓸 수 있고, 대시보드만 떠 있어도 사람이 데이터를 편집할 수 있다. 둘 다 떠 있으면 한쪽의 변경이 다른 쪽에 곧바로 반영된다.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## 2. 계층 구조
|
|
59
|
+
|
|
60
|
+
코드는 의존성 방향이 한쪽으로만 흐르는 계층으로 정리되어 있다. 패키지 별칭(`@careermate/*`)은 루트 `tsconfig.json`의 `paths`에 정의되어 있으며, 각 별칭은 해당 패키지의 `src/index.ts`를 가리킨다.
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
shared
|
|
64
|
+
│ (타입 + zod 스키마 + 라벨/상수. 의존성 없음)
|
|
65
|
+
▼
|
|
66
|
+
db
|
|
67
|
+
│ (node:sqlite 연결, 스키마/마이그레이션, 리포지토리, 경로/런타임)
|
|
68
|
+
▼
|
|
69
|
+
core
|
|
70
|
+
│ (비즈니스 유스케이스: get_application_context, save_* 등)
|
|
71
|
+
▼
|
|
72
|
+
┌───────────────────────────┬───────────────────────────┐
|
|
73
|
+
│ │ │
|
|
74
|
+
apps/web (REST API) packages/mcp-tools │
|
|
75
|
+
· routes.ts · 24개 도구 정의 │
|
|
76
|
+
· security/http/exports · 핸들러 → core 호출 │
|
|
77
|
+
└──────────┬────────────────┘
|
|
78
|
+
▼
|
|
79
|
+
apps/mcp (stdio 서버)
|
|
80
|
+
|
|
81
|
+
보조 패키지 (core / web / mcp-tools가 필요 시 사용):
|
|
82
|
+
parsers · 공고 텍스트 정제, 파일 텍스트 추출
|
|
83
|
+
exporters · md/html/txt 포맷팅
|
|
84
|
+
prompts · AI용 프롬프트 텍스트
|
|
85
|
+
workflows · 5종 워크플로우 가이드 데이터
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
핵심 규칙:
|
|
89
|
+
|
|
90
|
+
- `shared`는 아무것도 의존하지 않는 가장 아래 계층이다. 타입, zod 입력 스키마, 상태 라벨/순서 같은 상수만 담는다.
|
|
91
|
+
- `db`는 `shared`만 의존한다. SQLite 연결과 리포지토리(`profileRepo`, `jobRepo`, …)를 제공한다.
|
|
92
|
+
- `core`는 `db`와 `shared` 위에 비즈니스 유스케이스를 얹는다. 대시보드 API와 MCP 도구는 **같은 core 함수**를 호출하므로 두 진입점이 항상 동일하게 동작한다(예: `getApplicationContext`, `saveFitAnalysis`).
|
|
93
|
+
- `apps/web`와 `packages/mcp-tools`는 둘 다 core를 호출하는 **두 개의 진입점**이다. 하나는 HTTP로, 다른 하나는 MCP stdio로 같은 로직에 도달한다.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## 3. 요청 흐름
|
|
98
|
+
|
|
99
|
+
### 3.1 대시보드 요청 (HTTP GET/PUT/POST/DELETE)
|
|
100
|
+
|
|
101
|
+
브라우저가 `apps/web` 서버에 HTTP 요청을 보낸다. `apps/web/src/server.ts`의 핸들러가 다음 순서로 처리한다.
|
|
102
|
+
|
|
103
|
+
1. **보안 게이트** — 어떤 작업보다 먼저 `checkRequest(req)`가 실행된다(`apps/web/src/security.ts`). Host 허용목록(DNS 리바인딩 차단), Origin 허용목록(교차 출처 차단), 변경 요청(POST/PUT/PATCH/DELETE)에 대한 CSRF 세션 토큰을 검사한다. 거부되면 곧바로 403을 반환한다.
|
|
104
|
+
2. **API 라우팅** — 경로가 `/api/`로 시작하면 `Router`가 메서드+경로로 핸들러를 찾는다(`apps/web/src/routes.ts`). 없으면 404.
|
|
105
|
+
3. **본문 검증 + 처리** — 변경 요청 본문은 `readJsonBody`가 `@careermate/shared`의 zod 스키마로 검증한다. 검증을 통과한 입력만 core 함수나 리포지토리로 전달된다. 예: `PUT /api/profile`은 `ProfileInputSchema`로 검증 후 `saveProfile(input)`을 호출한다.
|
|
106
|
+
4. **응답 직렬화** — 핸들러는 평범한 객체를 반환하고 서버가 JSON으로 직렬화한다(`sendJson`). 내보내기 경로는 `__download` 마커가 붙은 객체를 반환해 파일 다운로드로 스트리밍된다(`sendDownload`).
|
|
107
|
+
5. **정적 서빙** — `/api/`가 아니면 정적 자산을 서빙한다. `/install`은 설치 페이지를, 그 외는 대시보드 SPA를 서빙하며 매칭 실패 시 `index.html`로 폴백한다. HTML을 서빙할 때 `injectToken`이 CSRF 토큰을 `<meta>` 태그로 주입한다.
|
|
108
|
+
|
|
109
|
+
오류는 `HttpError`(상태코드 포함)와 그 외 예외로 나뉜다. 후자는 일반화된 500 메시지만 응답하고, 이력서/자소서 본문이 로그나 응답에 새지 않도록 한다.
|
|
110
|
+
|
|
111
|
+
### 3.2 MCP 도구 호출 (stdio)
|
|
112
|
+
|
|
113
|
+
AI 클라이언트가 `apps/mcp` 서버에 MCP 프로토콜로 도구 호출을 보낸다(`apps/mcp/src/index.ts`).
|
|
114
|
+
|
|
115
|
+
1. **기동 시 DB 보장** — `main()`이 `getDb()`를 먼저 호출해 DB 생성·마이그레이션을 마친 뒤 도구를 서빙한다.
|
|
116
|
+
2. **도구 등록** — `@careermate/mcp-tools`의 `TOOLS` 배열을 순회하며 각 도구를 `server.registerTool`로 등록한다. 입력 스키마(zod), 제목/설명, `readOnlyHint`/`openWorldHint` 어노테이션이 함께 전달된다. `openWorldHint`는 항상 `false` — 외부 세계로 나가지 않는다.
|
|
117
|
+
3. **핸들러 실행** — 도구 호출이 오면 해당 핸들러가 인자를 받아 core 로직을 수행하고, 결과를 `toCallToolResult`로 MCP 응답 형태로 변환한다. 예외는 안전한 메시지로만 변환되어 문서 본문이 새지 않는다.
|
|
118
|
+
4. **전송 계층** — `StdioServerTransport`로 통신한다. **stdout은 MCP 전송 채널이므로 절대 로그를 쓰지 않는다.** 모든 로그는 stderr로만 나간다.
|
|
119
|
+
|
|
120
|
+
대시보드 API와 MCP 도구는 결국 같은 core 함수에 도달하므로, 한쪽에서 일어난 변경은 같은 SQLite를 통해 다른 쪽에 즉시 보인다. 이것이 "대시보드↔MCP 동일 DB 양방향"의 실체다.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## 4. 프로세스 간 런타임 핸드셰이크 (server.json)
|
|
125
|
+
|
|
126
|
+
두 프로세스는 서로를 네트워크로 탐색하지 않는다. 대신 데이터 디렉터리의 작은 JSON 파일(`~/.careermate/server.json`)을 통해 서로를 찾는다(`packages/db/src/runtime.ts`).
|
|
127
|
+
|
|
128
|
+
- **웹서버가 부팅하면** 자신의 라이브 주소를 기록한다. `startServer`가 포트 바인딩에 성공한 직후 `writeRuntimeInfo({ url, port, pid, started_at })`를 호출한다.
|
|
129
|
+
- **MCP 서버는** `open_dashboard`, `open_application` 같은 도구를 실행할 때 `readRuntimeInfo()`로 그 파일을 읽어, 실제로 떠 있는 대시보드의 올바른 URL/포트로 브라우저를 가리킨다.
|
|
130
|
+
- **생존 확인** — `isProcessAlive(pid)`로 기록된 pid의 프로세스가 살아 있는지 확인할 수 있어, 죽은 서버의 낡은 주소를 잡지 않는다.
|
|
131
|
+
- **정리** — 웹서버는 `SIGINT`/`SIGTERM`에서 `clearRuntimeInfo()`로 파일을 지운 뒤 종료한다.
|
|
132
|
+
|
|
133
|
+
`RuntimeInfo`의 형태는 다음과 같다.
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
interface RuntimeInfo {
|
|
137
|
+
url: string; // 예: "http://127.0.0.1:4319"
|
|
138
|
+
port: number;
|
|
139
|
+
pid: number;
|
|
140
|
+
started_at: string; // ISO 8601
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
쓰기/읽기 모두 실패를 비치명적으로 처리한다(핸드셰이크 파일이 없거나 깨져도 각 프로세스는 독립적으로 동작한다).
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 5. 보안 경계
|
|
149
|
+
|
|
150
|
+
CareerMate는 로컬에만 머무는 도구이지만, **로컬 서버는 사용자가 방문하는 임의의 웹페이지에서도 도달 가능**하다는 점을 전제로 방어한다(브라우저는 `127.0.0.1`로의 요청도 보낸다). 의사결정 배경은 [DECISIONS.md](./DECISIONS.md)의 "로컬-우선 보안 모델" 절에 있으며, 핵심 경계는 다음과 같다.
|
|
151
|
+
|
|
152
|
+
- **로프백 전용 바인딩** — 서버는 항상 `127.0.0.1`에 바인딩하며 `0.0.0.0`에는 절대 바인딩하지 않는다. 네트워크에서 도달 불가.
|
|
153
|
+
- **Host 허용목록** — `Host` 헤더가 로프백 이름(`localhost`/`127.0.0.1`/`[::1]`/`::1`)이 아니면 거부. DNS 리바인딩 차단.
|
|
154
|
+
- **Origin 허용목록** — `Origin`이 있으면 로프백이어야 한다. 교차 출처 페이지의 요청을 거부.
|
|
155
|
+
- **CSRF 세션 토큰** — 서버 기동 시 1회 발급한 랜덤 토큰을 서빙되는 HTML의 `<meta name="careermate-token">`에 주입한다. 변경 요청(POST/PUT/PATCH/DELETE)은 이 토큰을 `x-careermate-token` 헤더로 제시해야 한다. 동일 출처 스크립트만 토큰을 읽을 수 있고, 비교는 타이밍 안전 비교를 쓴다.
|
|
156
|
+
- **허용적 CORS 미발급** — 어떤 응답에도 허용적 CORS 헤더를 붙이지 않으므로, 설령 요청이 통과해도 교차 출처 페이지는 응답을 읽지 못한다.
|
|
157
|
+
- **정적 경로 traversal 차단**, **본문 크기 제한(8MB)**, **민감 본문 미노출** — 이력서/자소서 본문은 로그·에러 응답에 절대 노출하지 않는다.
|
|
158
|
+
- **MCP 측 경계** — MCP 서버는 stdout에 절대 쓰지 않고(전송 채널 보호), 도구 어노테이션의 `openWorldHint=false`로 외부 세계 접근이 없음을 명시한다. 예외도 안전한 메시지로만 변환한다.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## 6. 무빌드(tsx)·무네이티브(node:sqlite) 선택 요약
|
|
163
|
+
|
|
164
|
+
근거의 상세는 [DECISIONS.md](./DECISIONS.md)에 있다. 요지는 다음과 같다.
|
|
165
|
+
|
|
166
|
+
- **무빌드 실행 (tsx)** — 컴파일 산출물을 만들지 않고 TypeScript를 그대로 실행한다. `tsconfig.json`은 `noEmit: true`, `allowImportingTsExtensions: true`, `moduleResolution: "Bundler"`로 설정되어 `.ts` 확장자를 직접 import한다. 빌드 단계가 없어 설치·기여·디버깅이 단순하다.
|
|
167
|
+
- **무네이티브 SQLite (node:sqlite)** — Node 내장 `node:sqlite`의 `DatabaseSync`를 사용한다. `better-sqlite3` 같은 네이티브 모듈을 컴파일할 필요가 없으므로 설치 시 C/C++ 툴체인이 필요 없고, 플랫폼별 빌드 실패 위험이 없다. 연결은 WAL + `foreign_keys = ON` + `busy_timeout = 5000`으로 열려 두 프로세스 동시성을 안전하게 다룬다(`packages/db/src/connection.ts`).
|
|
168
|
+
- **그 외** — zod로 입력 검증, MCP SDK(`@modelcontextprotocol/sdk`)로 도구 노출, 대시보드는 프레임워크/CDN 없는 바닐라 JS + 자체 CSS 디자인 시스템.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 7. 확장 포인트
|
|
173
|
+
|
|
174
|
+
CareerMate의 두 표면(MCP 도구, REST API)은 각각 다른 클라이언트로 확장된다.
|
|
175
|
+
|
|
176
|
+
- **AI 클라이언트(MCP)** — MCP stdio 서버는 표준 MCP 프로토콜만 따르므로, MCP를 지원하는 어떤 클라이언트와도 연결된다.
|
|
177
|
+
- **Claude (Desktop Extension)** — MCP 서버를 `npm run mcp`로 기동하도록 등록.
|
|
178
|
+
- **ChatGPT App / Gemini / Cursor** — 동일한 MCP 서버를 각 클라이언트의 도구/커넥터로 등록. 새 클라이언트를 지원하려면 도구 코드 변경 없이 클라이언트 측 등록 설정만 추가하면 된다.
|
|
179
|
+
- 새 능력이 필요하면 `packages/mcp-tools`에 도구를 추가하고 핸들러를 core에 연결한다. 같은 core 함수를 대시보드 API에서도 노출하면 두 표면이 자동으로 동기화된다.
|
|
180
|
+
- **REST API** — `apps/web`의 REST 표면은 대시보드 외의 클라이언트(스크립트, 자동화)도 사용할 수 있다. 단, 변경 요청은 CSRF 토큰과 로프백 제약을 따라야 한다.
|
|
181
|
+
- **데스크톱 패키징(Tauri/Electron)** — 두 프로세스 모델과 로컬 SQLite 구조는 데스크톱 셸로 감싸기에 적합하다. 웹서버를 내장 웹뷰로 띄우고 MCP 서버를 함께 번들링하면 단일 앱으로 배포할 수 있다. 데이터 위치는 `CAREERMATE_DATA_DIR`로 제어되므로 패키징 환경에 맞게 재배치 가능하다.
|
|
182
|
+
- **데이터 위치/포트 제어** — 환경변수 `CAREERMATE_DATA_DIR`(데이터 디렉터리), `CAREERMATE_PORT`(선호 포트), `CAREERMATE_NO_OPEN`(브라우저 자동 열기 비활성)으로 실행 환경을 조정한다.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 8. 패키지별 책임 표
|
|
187
|
+
|
|
188
|
+
| 패키지 / 앱 | 별칭 | 책임 | 주 의존 |
|
|
189
|
+
| --- | --- | --- | --- |
|
|
190
|
+
| `packages/shared` | `@careermate/shared` | 타입, zod 입력 스키마, 상태/문서 라벨·순서 등 상수. 모든 계층이 공유. | (없음) |
|
|
191
|
+
| `packages/db` | `@careermate/db` | 단일 `DatabaseSync` 연결(WAL), 12개 테이블 스키마/마이그레이션, 리포지토리, 데이터 경로(`paths.ts`), 런타임 핸드셰이크(`runtime.ts`). | shared |
|
|
192
|
+
| `packages/core` | `@careermate/core` | 비즈니스 유스케이스. `getApplicationContext`(핵심), `saveProfile`, `saveJobPosting`, `saveFitAnalysis`, `saveCoverLetterVersion`, `updateApplicationStatus`, `saveInterviewPrep`, 온보딩/요약/활동 조회 등. 두 진입점이 공유. | db, shared |
|
|
193
|
+
| `packages/mcp-tools` | `@careermate/mcp-tools` | 24개 MCP 도구 정의(`TOOLS`)와 핸들러, `toCallToolResult`. 도구 핸들러는 core를 호출. | core, shared, parsers/prompts/workflows |
|
|
194
|
+
| `packages/parsers` | `@careermate/parsers` | 공고 텍스트 정제(`cleanJobPosting`), 업로드 파일 텍스트 추출(`extractText`). | shared |
|
|
195
|
+
| `packages/exporters` | `@careermate/exporters` | md/html/txt 내보내기 포맷팅(`ExportResult`). | shared |
|
|
196
|
+
| `packages/prompts` | `@careermate/prompts` | 사용자 AI에 전달할 프롬프트 텍스트(`PROMPTS`). | shared |
|
|
197
|
+
| `packages/workflows` | `@careermate/workflows` | 5종 워크플로우 가이드 데이터(`WORKFLOWS`: onboarding, analyze_job, write_cover_letter, manage_application_status, prepare_interview). | shared |
|
|
198
|
+
| `apps/web` | (앱) | HTTP 서버: 보안 게이트 → REST API 라우터 → 정적 대시보드/설치 페이지. 로프백 바인딩, 포트 폴백, `server.json` 발행. | core, db, shared, parsers, exporters, prompts, workflows |
|
|
199
|
+
| `apps/mcp` | (앱) | MCP stdio 서버. 도구 등록, stdio 전송, 같은 SQLite 공유. | mcp-tools, db |
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 관련 문서
|
|
204
|
+
|
|
205
|
+
- 기술 의사결정 근거 및 보안 모델: [DECISIONS.md](./DECISIONS.md)
|
|
206
|
+
- 데이터 모델(테이블 12개): [DATA_MODEL.md](./DATA_MODEL.md)
|
|
207
|
+
- MCP 도구 목록(24개): [MCP_TOOLS.md](./MCP_TOOLS.md)
|
|
208
|
+
- 워크플로우: [WORKFLOWS.md](./WORKFLOWS.md)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# v1 변경 기록 (CHANGES_V1)
|
|
2
|
+
|
|
3
|
+
이 문서는 CareerMate **v1 재설계**의 배경·목표와 구체적인 구조 변경을 기록한다. 무엇을
|
|
4
|
+
왜 바꿨는지, 무엇을 그대로 유지하고 무엇을 범위에서 뺐는지, 그리고 아직 남은 TODO를
|
|
5
|
+
한곳에서 추적하기 위한 문서다. 기준일: **2026-06-14**.
|
|
6
|
+
|
|
7
|
+
관련 문서: [설계 결정(ADR)](./DECISIONS.md), [로드맵](./ROADMAP.md), [TODO](./TODO.md),
|
|
8
|
+
[AI 런북(/INSTALL.md)](../INSTALL.md)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. 배경 / 목표
|
|
13
|
+
|
|
14
|
+
CareerMate는 "AI 서비스"가 아니다. **로컬 커리어 워크스페이스**다. 분석·작성 같은
|
|
15
|
+
지능 작업은 사용자의 로컬 AI 에이전트가 수행하고, CareerMate는 구조화된 커리어 데이터를
|
|
16
|
+
로컬에 저장·제공하는 역할만 한다(LLM 비내장, MCP-우선 — [DECISIONS.md #4](./DECISIONS.md) 참조).
|
|
17
|
+
|
|
18
|
+
v1의 목표는 이 정체성을 제품 표면과 문서 전반에 일관되게 반영하고, **로컬 stdio MCP를
|
|
19
|
+
지원하는 세 가지 사용 방식**을 1급으로 끌어올리는 것이다.
|
|
20
|
+
|
|
21
|
+
| 순위 | 사용 방식 | 영구 지시 파일 | MCP 등록 위치 |
|
|
22
|
+
|---|---|---|---|
|
|
23
|
+
| 1 | Claude Desktop | (번들 Node) | `.mcpb` 원클릭 설치 → "Claude Desktop 연결" |
|
|
24
|
+
| 2 | Claude Code | 루트 `CLAUDE.md` | 프로젝트 `.mcp.json` (CareerMate 폴더 내) |
|
|
25
|
+
| 3 | Codex (OpenAI Codex CLI) | 루트 `AGENTS.md` | `~/.codex/config.toml` `[mcp_servers.careermate]` |
|
|
26
|
+
|
|
27
|
+
> ChatGPT·Gemini **웹/모바일은 동작하지 않는다.** 이들은 클라우드에서 돌며 원격 URL
|
|
28
|
+
> MCP만 쓸 수 있어, 로컬 stdio 서버에 닿지 못한다. 로컬에서 실행되는 Claude Desktop /
|
|
29
|
+
> Claude Code / Codex만 연결된다. (보조: Cursor / Cline / Windsurf 등 다른 로컬 stdio
|
|
30
|
+
> MCP 클라이언트도 `npm run init`으로 동작하나, 1급으로 내세우지 않는다.)
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 2. 변경 사항
|
|
35
|
+
|
|
36
|
+
### 2.1 Claude Desktop 재배치
|
|
37
|
+
- 기존: `.mcpb` 원클릭이 사실상 **메인 설치 경로**처럼 제시됨.
|
|
38
|
+
- 변경: 여러 연결 방법 중 **하나**인 "Claude Desktop 연결"로 재배치. `.mcpb` 경로는
|
|
39
|
+
그대로 유지하되 단일 정답이 아님을 명확히 함.
|
|
40
|
+
|
|
41
|
+
### 2.2 Claude Code 지원 추가
|
|
42
|
+
- 루트 `CLAUDE.md`를 영구 지시 파일로 사용.
|
|
43
|
+
- MCP 등록은 CareerMate 폴더 안의 **프로젝트 스코프 `.mcp.json`**
|
|
44
|
+
(top-level `mcpServers`, 서버별 `{"type":"stdio","command":...,"args":[...]}`).
|
|
45
|
+
- 대안 CLI: `claude mcp add --scope project --transport stdio careermate -- node --experimental-sqlite <path>`.
|
|
46
|
+
- Claude Code는 첫 실행 시 프로젝트 서버에 대해 **1회 승인**을 요구한다.
|
|
47
|
+
|
|
48
|
+
### 2.3 Codex 지원 추가
|
|
49
|
+
- 루트 `AGENTS.md`를 영구 지시 파일로 사용.
|
|
50
|
+
- MCP 등록은 `~/.codex/config.toml`의 `[mcp_servers.careermate]` 테이블
|
|
51
|
+
(`command=...`, `args=[...]`).
|
|
52
|
+
- 대안 CLI: `codex mcp add careermate -- node --experimental-sqlite <path>`.
|
|
53
|
+
- Codex 안에서 `/mcp` 명령으로 검증.
|
|
54
|
+
|
|
55
|
+
### 2.4 `scripts/init.ts` 확장 (구현 완료, 고정)
|
|
56
|
+
- 타깃에 `claude-code` / `codex` 추가(기존 `claude`, `cursor`와 함께).
|
|
57
|
+
- **감지 기반 기본 동작**: 설정 파일 또는 그 상위 폴더가 존재하면 해당 클라이언트를
|
|
58
|
+
"감지"로 보고 자동 등록. Claude Code 프로젝트 `.mcp.json`은 **항상** CareerMate
|
|
59
|
+
폴더에 기록.
|
|
60
|
+
- 플래그 추가: `--print`(쓰지 않고 출력), `--npx`(npx 실행 형태로 등록),
|
|
61
|
+
`--all-clients`(감지 무관 전부 기록), `--client <name>`(하나만 타깃).
|
|
62
|
+
- **Codex TOML 머지**: 기존 설정을 타임스탬프 백업한 뒤, 사용자 env와 무관한 TOML
|
|
63
|
+
테이블을 보존하면서 병합.
|
|
64
|
+
|
|
65
|
+
### 2.5 설치 문서 이원화
|
|
66
|
+
- 루트 **`/INSTALL.md` 신설** — AI 에이전트가 따라가는 런북.
|
|
67
|
+
- `docs/INSTALL.md`는 **수동 설정 레퍼런스**로 전환.
|
|
68
|
+
- `docs/SUPPORTED_AI_APPS.md` 신설 — 지원/미지원 AI 앱 정리(웹이 왜 안 되는지 포함).
|
|
69
|
+
- `docs/TODO.md` 신설 — 미해결 과제 추적.
|
|
70
|
+
|
|
71
|
+
### 2.6 기타
|
|
72
|
+
- `package.json` publish 메타데이터 보강: `author`, `keywords`, `publishConfig`.
|
|
73
|
+
- `.gitignore`에 `.mcp.json` 추가(로컬 등록 산출물 커밋 방지).
|
|
74
|
+
- README / install-page 재배치 — 세 가지 1급 사용 방식 구조에 맞춰 재구성.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 3. 유지 / 비범위
|
|
79
|
+
|
|
80
|
+
### 유지 (삭제하지 않음)
|
|
81
|
+
- 패키지: `packages/{shared,db,core,mcp-tools,exporters,parsers,prompts,workflows}`
|
|
82
|
+
- 앱: `apps/{web,mcp}`, 그리고 `install-page/`, `docs/`, `scripts/`
|
|
83
|
+
- 기존 5종 워크플로우(onboarding, analyze_job, write_cover_letter,
|
|
84
|
+
manage_application_status, prepare_interview) 전부.
|
|
85
|
+
- `.mcpb` 빌드 경로(`npm run build:mcpb` → `dist/careermate.mcpb`).
|
|
86
|
+
|
|
87
|
+
### v1 비범위 (제외하되 구조는 열어둠)
|
|
88
|
+
Electron · exe · Gateway · WebSocket · oneTimeToken · 클라우드 저장/동기화 · 인증 서버.
|
|
89
|
+
구조는 이후 추가를 수용할 수 있게 열어두며, 상세는 [docs/TODO.md](./TODO.md)와
|
|
90
|
+
[ROADMAP.md](./ROADMAP.md)를 참조한다.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## 4. 알려진 미해결 TODO
|
|
95
|
+
|
|
96
|
+
| 항목 | 현재 상태 | 비고 |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| npm 게시 | **완료** | `careermate@0.1.0` 공개 게시됨. `npx -y careermate init` 동작. 소스 기반도 유효. |
|
|
99
|
+
| `.mcpb` Release | **생성됨(비공개)** | Release `v0.1.0`에 `careermate.mcpb` 첨부. 저장소가 비공개라 공개 다운로드는 아직 불가(공개 전환 시 활성화). |
|
|
100
|
+
| 공개 repo URL | **확정** | 정본 `osntak/CareerMate`(git remote 기준). 현재 비공개. |
|
|
101
|
+
| THIRD_PARTY_NOTICES | **보완 필요** | 서드파티 고지 정리 미완. |
|
|
102
|
+
|
|
103
|
+
> 갱신(2026-06-15): npm 게시 · Release 생성 · repo URL 확정 완료. 남은 결정은 저장소 공개 전환 여부(공개 시 `.mcpb` 공개 다운로드 활성화)와 THIRD_PARTY_NOTICES 보완.
|