careermate 0.1.0 → 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 +22 -17
- package/THIRD_PARTY_NOTICES.md +15 -0
- package/apps/mcp/src/index.ts +4 -3
- package/apps/web/DESIGN_GUIDE.md +2 -2
- 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/icon-180.png +0 -0
- package/apps/web/public/index.html +14 -3
- package/apps/web/public/lib.js +135 -23
- package/apps/web/public/pages/applications.js +18 -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 +21 -14
- package/apps/web/public/styles.css +105 -38
- package/apps/web/src/http.ts +31 -4
- package/apps/web/src/info.ts +9 -22
- package/apps/web/src/routes.ts +19 -14
- package/apps/web/src/server.ts +34 -29
- package/apps/web/src/settings.ts +35 -17
- package/bin/careermate.mjs +8 -5
- package/dist/doctor.mjs +78908 -0
- 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/favicon.svg +1 -0
- package/dist/install-page/icon-180.png +0 -0
- package/dist/install-page/icon-512.png +0 -0
- package/dist/install-page/index.html +328 -225
- package/dist/install-page/landing.js +54 -0
- package/dist/install-page/llms-install.txt +202 -0
- package/dist/install-page/og.png +0 -0
- package/dist/install-page/shots/applications-dark.png +0 -0
- package/dist/install-page/shots/applications-light.png +0 -0
- package/dist/install-page/shots/documents-dark.png +0 -0
- package/dist/install-page/shots/documents-light.png +0 -0
- package/dist/install-page/shots/home-dark.png +0 -0
- package/dist/install-page/shots/home-light.png +0 -0
- package/dist/install-page/shots/interview-dark.png +0 -0
- package/dist/install-page/shots/interview-light.png +0 -0
- package/dist/install-page/shots/jobs-dark.png +0 -0
- package/dist/install-page/shots/jobs-light.png +0 -0
- package/dist/install-page/shots/settings-dark.png +0 -0
- package/dist/install-page/shots/settings-light.png +0 -0
- package/dist/install-page/start.html +323 -0
- package/dist/install-page/style.css +128 -4
- package/dist/mcp.mjs +74225 -2007
- package/dist/{mcpb-stage/dist/web.mjs → migrate.mjs} +142 -2660
- package/dist/pdf.worker.min.mjs +28 -0
- package/dist/public/app.js +34 -2
- package/dist/public/icon-180.png +0 -0
- package/dist/public/index.html +14 -3
- package/dist/public/lib.js +135 -23
- package/dist/public/pages/applications.js +18 -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 +21 -14
- package/dist/public/styles.css +105 -38
- package/dist/web.mjs +39950 -369
- package/docs/ARCHITECTURE.md +5 -5
- package/docs/DECISIONS.md +1 -1
- package/docs/DEMO.md +6 -6
- package/docs/MCP_TOOLS.md +68 -9
- package/docs/ROADMAP.md +11 -10
- package/docs/RUNTIME_DISTRIBUTION_RESEARCH.md +135 -0
- package/docs/START_WORKFLOW.md +19 -7
- package/docs/SUPPORTED_AI_APPS.md +85 -33
- package/docs/TODO.md +1 -1
- package/docs/WORKFLOWS.md +8 -6
- package/docs/superpowers/specs/2026-06-16-demo-dashboard-deploy-design.md +64 -0
- package/package.json +8 -3
- package/packages/core/src/services.ts +42 -1
- package/packages/db/src/connection.ts +30 -0
- package/packages/db/src/index.ts +1 -0
- package/packages/db/src/paths.ts +11 -0
- package/packages/db/src/repositories.ts +109 -63
- package/packages/db/src/schema.ts +118 -0
- package/packages/exporters/src/html.ts +9 -12
- 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 +269 -65
- 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/enums.ts +39 -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 +46 -8
- package/scripts/build-mcpb.mjs +60 -4
- package/scripts/doctor.ts +16 -3
- package/scripts/gen-og.mjs +86 -0
- package/scripts/init.ts +32 -3
- package/scripts/seed.ts +5 -5
- package/scripts/shots-seed.ts +192 -0
- package/scripts/shots.ts +89 -0
- package/tsconfig.json +3 -1
- package/dist/careermate.mcpb +0 -0
- package/dist/mcp-smoke.err +0 -3
- package/dist/mcpb-stage/README.md +0 -219
- package/dist/mcpb-stage/dist/install-page/index.html +0 -434
- package/dist/mcpb-stage/dist/install-page/style.css +0 -407
- package/dist/mcpb-stage/dist/install-page/vercel.json +0 -20
- package/dist/mcpb-stage/dist/mcp.mjs +0 -23704
- package/dist/mcpb-stage/dist/public/app.js +0 -118
- package/dist/mcpb-stage/dist/public/index.html +0 -41
- package/dist/mcpb-stage/dist/public/lib.js +0 -282
- package/dist/mcpb-stage/dist/public/pages/applications.js +0 -98
- package/dist/mcpb-stage/dist/public/pages/documents.js +0 -446
- package/dist/mcpb-stage/dist/public/pages/home.js +0 -263
- package/dist/mcpb-stage/dist/public/pages/interview.js +0 -230
- package/dist/mcpb-stage/dist/public/pages/jobs.js +0 -494
- package/dist/mcpb-stage/dist/public/pages/profile.js +0 -576
- package/dist/mcpb-stage/dist/public/pages/settings.js +0 -233
- package/dist/mcpb-stage/dist/public/styles.css +0 -420
- package/dist/mcpb-stage/manifest.json +0 -40
- package/docs/INSTALL.md +0 -148
- package/docs/INSTALL_AND_USAGE.md +0 -99
- package/install-page/index.html +0 -474
- package/install-page/style.css +0 -391
- package/install-page/vercel.json +0 -20
- /package/dist/{mcpb-stage/dist/public → install-page}/fonts/PretendardVariable.woff2 +0 -0
|
@@ -1,60 +1,112 @@
|
|
|
1
|
-
# 지원 AI 앱 (Supported AI Apps)
|
|
1
|
+
# 지원 AI 앱 & 설치 레퍼런스 (Supported AI Apps)
|
|
2
2
|
|
|
3
|
-
CareerMate는 LLM이 없는 **로컬 MCP 도구**입니다.
|
|
3
|
+
CareerMate는 LLM이 없는 **로컬 MCP 도구**입니다. *내 컴퓨터에서 로컬 프로그램을 직접 실행할 수 있는* AI 앱에서만 동작합니다.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
| --- | --- | --- |
|
|
7
|
-
| **Claude Desktop** | — | `.mcpb` 번들 (원클릭) |
|
|
8
|
-
| **Claude Code** | 루트 `CLAUDE.md` | 프로젝트 `.mcp.json` |
|
|
9
|
-
| **Codex** (OpenAI Codex CLI) | 루트 `AGENTS.md` | `~/.codex/config.toml` |
|
|
5
|
+
> **가장 쉬운 사람용 설치 안내는 https://careermate.life (랜딩)** 입니다. 이 문서는 **지원 앱 매트릭스 + 클라이언트별 수동 MCP 설정 + 알려진 제약**을 모은 레퍼런스입니다. AI 에이전트가 따라가는 단계별 런북은 루트 [`/INSTALL.md`](../INSTALL.md), 설치 후 사용 흐름은 [docs/START_WORKFLOW.md](./START_WORKFLOW.md)를 보세요.
|
|
10
6
|
|
|
11
7
|
---
|
|
12
8
|
|
|
13
|
-
##
|
|
9
|
+
## 설치는 크게 3가지 — "누가 설치하느냐"
|
|
14
10
|
|
|
15
|
-
|
|
11
|
+
| | 무엇 | Node | 대상 |
|
|
12
|
+
|---|---|---|---|
|
|
13
|
+
| **1. 에이전트가 대신 설치** | 작업 폴더에서 **한 문장** → AI가 `careermate.life/llms-install.txt`를 읽고 npm 설치 + MCP 등록 | 필요(에이전트가 winget/brew 또는 nodejs.org로 도와줌) | Claude Code(데스크톱 **"Code" 탭** / CLI)·Codex CLI·Gemini CLI·Cursor 등 *명령 실행 가능한 에이전트* |
|
|
14
|
+
| **2. npm 명령 직접** | 터미널에 `npx -y careermate init` 한 줄(감지된 앱 자동 등록) | 필요 | Claude Desktop·Codex·Cursor 등(그 외는 `--print`로 수동) |
|
|
15
|
+
| **3. `.mcpb`** | 파일 하나를 설정에서 추가 | **불필요**(Claude Desktop 내장) | **Claude Desktop "채팅" 앱 전용** |
|
|
16
16
|
|
|
17
|
-
1. **Claude Desktop
|
|
18
|
-
2. **`careermate.mcpb` 받기** — Release `v0.1.0`에 첨부돼 있으나 저장소가 **비공개**라 접근 권한이 있어야 받을 수 있습니다. 그 외에는 소스에서 `npm run build:mcpb`로 직접 빌드하세요.
|
|
19
|
-
3. **더블클릭** (또는 Settings → Extensions 로 끌어다 놓기)
|
|
20
|
-
4. **Claude Desktop 재시작**
|
|
17
|
+
**1·2는 같은 메커니즘**(Node + npm/npx + MCP 등록) — 에이전트가 치느냐 / 내가 치느냐의 차이일 뿐입니다. 그래서 1·2의 "대상" 제품군이 서로 **겹칩니다**(같은 Claude Code·Codex·Cursor 등). 어느 앱이 어느 방식을 쓰는지는 아래 **앱 × 방식** 매트릭스로 보세요. **3(`.mcpb`)만 Claude Desktop "채팅" 앱 전용**이라 성격이 다릅니다(내장 Node 사용, 단 버전<22.5면 불안정).
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
## Claude Code
|
|
19
|
+
### 앱 × 방식
|
|
25
20
|
|
|
26
|
-
|
|
21
|
+
| 앱 | 1 에이전트 | 2 npm직접 | 3 .mcpb | 영구 지침 | MCP 등록 위치 |
|
|
22
|
+
|---|---|---|---|---|---|
|
|
23
|
+
| **Claude Desktop "채팅"** | — | ✅(config 등록) | ✅ | — | `.mcpb` 또는 `claude_desktop_config.json` |
|
|
24
|
+
| **Claude Code (Code 탭 / CLI)** | ✅ | ✅ | — | 루트 `CLAUDE.md` | 프로젝트 `.mcp.json` |
|
|
25
|
+
| **Codex CLI** (⚠️ 데스크톱 앱 ✗) | ✅ | ✅ | — | 루트 `AGENTS.md` | `~/.codex/config.toml` |
|
|
26
|
+
| **Gemini CLI / Antigravity** | ✅ | ✅ | — | — | Gemini CLI `~/.gemini/settings.json` · Antigravity `~/.gemini/.../mcp_config.json` |
|
|
27
|
+
| **Cursor · Cline · Windsurf** | ✅ | ✅ | — | — | `~/.cursor/mcp.json` 등 |
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
2. 프롬프트 입력:
|
|
30
|
-
> "CareerMate를 설치하고 설정해줘. INSTALL.md를 따라 진행해줘."
|
|
29
|
+
> **Gemini CLI → Antigravity 전환(2026-06):** Google이 소비자(무료/Pro/Ultra)용 Gemini CLI를 **2026-06-18부터 Antigravity CLI로 전환**합니다(Gemini CLI repo는 오픈소스로 유지·엔터프라이즈 계속). MCP 스키마는 동일(`mcpServers` + `command`/`args`/`env`)합니다. `careermate init`은 **Gemini CLI(`~/.gemini/settings.json`)를 자동 등록**하고, **Antigravity는 설정 파일 경로가 유동적이라 후보 파일이 이미 있을 때만** 등록합니다(없으면 위 mcpServers 블록을 해당 파일에 수동 추가).
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
### 동작하지 않는 것
|
|
32
|
+
**ChatGPT · Gemini · Claude의 웹·모바일 앱은 안 됩니다.** 클라우드에서 실행되어 **원격 URL 기반 MCP만** 지원하므로, 내 컴퓨터의 로컬 stdio 서버에 직접 닿을 수 없습니다.
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
36
|
-
##
|
|
36
|
+
## 구독·준비물 (2026)
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
- **Claude Code**(Code 탭/CLI): **Pro 이상**(무료 플랜 불가) 또는 `ANTHROPIC_API_KEY` 종량제.
|
|
39
|
+
- **Codex**: **ChatGPT Plus 이상** 로그인으로 충분(API 키 불필요).
|
|
40
|
+
- **`.mcpb`**(Claude Desktop 채팅 확장): **무료 플랜에서도 설치** 가능(안정성이 변수).
|
|
41
|
+
- **터미널/npx**: 무료(Node만). 연결할 앱의 구독 정책은 별개.
|
|
39
42
|
|
|
40
|
-
1. **
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
> **런타임.** CareerMate 서버는 Node 프로그램이라 **Node.js 22.5+ 가 필요**합니다 — 위 1·2 경로 공통(없으면 에이전트가 winget/brew, 안 되면 [nodejs.org](https://nodejs.org) LTS 설치 파일). **Node가 아예 필요 없는 길은 `.mcpb` 하나뿐**인데 그건 내장 Node 버전에 의존해 불안정할 수 있습니다 — **"완전 무설치 + 안정"을 동시에 주는 길은 아직 없습니다.**
|
|
44
|
+
>
|
|
45
|
+
> **Windows + Claude Code(Code 탭)**: [Git for Windows](https://git-scm.com/download/win) 사전 설치가 필요합니다(앱이 없으면 "Git is required" 안내). Claude Code가 **세션 격리(git worktree)와 내부 POSIX 셸 도구**에 Git을 쓰기 때문이며 CareerMate와는 무관합니다. macOS는 Git 기본 포함이라 불필요.
|
|
46
|
+
|
|
47
|
+
---
|
|
43
48
|
|
|
44
|
-
|
|
49
|
+
## 작업 폴더 안에 설치 (권장 — GUI 앱·샌드박스 친화)
|
|
50
|
+
|
|
51
|
+
Claude "Code" 탭이나 Codex처럼 **작업 폴더(cwd)를 잡고** 쓰는 경우, 설치물과 데이터를 그 폴더 **안에** 두면 (1) 폴더만 백업·이동하면 데이터가 따라오고 (2) 샌드박스가 cwd 하위 쓰기를 허용해 마찰이 줄어듭니다. `<WS>`를 작업 폴더 절대경로라 할 때:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
npm install --prefix "<WS>/.cm/app" careermate
|
|
55
|
+
```
|
|
56
|
+
데이터는 `CAREERMATE_DATA_DIR` 환경변수로 작업 폴더 안에 고정합니다(기본값 `~/.careermate` 대신). 아래 MCP 등록의 `env`에 넣으면 됩니다:
|
|
57
|
+
```
|
|
58
|
+
CAREERMATE_DATA_DIR = <WS>/.cm/data
|
|
59
|
+
```
|
|
60
|
+
- `<BIN>` = `<WS>/.cm/app/node_modules/careermate/bin/careermate.mjs` (이 셸이 `--experimental-sqlite`를 알아서 붙임).
|
|
61
|
+
- 데이터 폴더 이름을 `.git`·`.codex`·`.agents`로 두지 마세요(앱이 읽기 전용으로 보호).
|
|
62
|
+
- **포터블 Node(고급):** 시스템에 Node 22.5+가 없으면, 작업 폴더 안에 포터블 Node를 두고 `command`를 그 `node` 절대경로로 가리켜도 동작합니다. 단 다운로드 용량·OS 보안 경고·폴더 이동 시 경로 갱신 부담이 있어, 지금은 "시스템 Node 1회 설치"가 더 단순합니다.
|
|
45
63
|
|
|
46
64
|
---
|
|
47
65
|
|
|
48
|
-
##
|
|
66
|
+
## 클라이언트별 수동 MCP 설정 (방식 2의 수동판)
|
|
67
|
+
|
|
68
|
+
대부분 `npx -y careermate init`(또는 소스에서 `npm run init`)이 자동 등록하므로 **수동 설정은 보통 불필요**합니다. 직접 붙여넣어야 할 때만 참고하세요. `npm run init -- --print`로 아래 스니펫을 출력할 수도 있습니다.
|
|
49
69
|
|
|
50
|
-
|
|
70
|
+
**경로 규칙** — 아래 `<BIN>`은 **실제 절대경로**로 바꾸세요(`~`·`$HOME`은 설정 파일 안에서 확장 안 됨).
|
|
71
|
+
- **Windows**: JSON에서 역슬래시는 `\\`로 두 번(`C:\\...\\careermate.mjs`). TOML은 **작은따옴표 리터럴**(`'C:\...\careermate.mjs'`)을 쓰면 한 번만.
|
|
72
|
+
- **macOS/Linux**: `/` 그대로.
|
|
51
73
|
|
|
52
|
-
|
|
74
|
+
### Claude Desktop (채팅앱 수동)
|
|
75
|
+
설정 파일: Windows `%APPDATA%\Claude\claude_desktop_config.json` · macOS `~/Library/Application Support/Claude/claude_desktop_config.json` (Settings → Developer → Edit Config로도 열림)
|
|
76
|
+
```json
|
|
77
|
+
{ "mcpServers": { "careermate": { "command": "node", "args": ["<BIN>", "mcp"] } } }
|
|
78
|
+
```
|
|
53
79
|
|
|
54
|
-
|
|
80
|
+
### Claude Code (프로젝트 `.mcp.json`, 작업 폴더 최상단)
|
|
81
|
+
```json
|
|
82
|
+
{ "mcpServers": { "careermate": { "type": "stdio", "command": "node", "args": ["<BIN>", "mcp"], "env": { "CAREERMATE_DATA_DIR": "<WS>/.cm/data" } } } }
|
|
83
|
+
```
|
|
84
|
+
또는 CLI: `claude mcp add --scope project --transport stdio careermate -- node <BIN> mcp` · 첫 실행 시 **1회 승인**.
|
|
85
|
+
|
|
86
|
+
### Codex CLI (`~/.codex/config.toml`)
|
|
87
|
+
```toml
|
|
88
|
+
[mcp_servers.careermate]
|
|
89
|
+
command = "node"
|
|
90
|
+
args = ["<BIN>", "mcp"]
|
|
91
|
+
# 작업 폴더 안에 데이터: env = { CAREERMATE_DATA_DIR = "<WS>/.cm/data" }
|
|
92
|
+
```
|
|
93
|
+
또는 CLI: `codex mcp add careermate -- node <BIN> mcp` · 등록 후 `/mcp`로 확인.
|
|
94
|
+
|
|
95
|
+
### Cursor / Cline / Windsurf
|
|
96
|
+
`~/.cursor/mcp.json`(전역) 또는 `.cursor/mcp.json`(프로젝트) 등에 위 Claude Code와 같은 `mcpServers` 블록. 추가 후 클라이언트 재시작.
|
|
97
|
+
|
|
98
|
+
> **소스를 클론한 개발자**는 `<BIN>` 대신 TS 소스를 직접 가리킬 수 있습니다(빌드 불필요):
|
|
99
|
+
> `node --no-warnings --experimental-sqlite --import tsx <careermate>/apps/mcp/src/index.ts mcp`
|
|
55
100
|
|
|
56
101
|
---
|
|
57
102
|
|
|
58
|
-
|
|
103
|
+
## 알려진 제약 · 트러블슈팅
|
|
104
|
+
|
|
105
|
+
- **Codex 데스크톱 앱(⚠️ CLI 권장):** [#24718](https://github.com/openai/codex/issues/24718)(OPEN) 시작 시 전역 `config.toml`을 재작성해 등록이 사라질 수 있음 + [#13025](https://github.com/openai/codex/issues/13025)(OPEN) 프로젝트 `.codex/config.toml` 무시. 그래서 **데스크톱 앱은 비권장, CLI를 쓰세요.** 설치 단계 `npm install`은 기본 샌드박스에서 네트워크가 꺼져 있어 **1회 허용**(또는 `[sandbox_workspace_write] network_access = true`)이 필요. (단 **MCP 서버 자체는 샌드박스 밖에서 돌아** 연결 후 데이터 읽기/쓰기는 막히지 않습니다.)
|
|
106
|
+
- **Claude Desktop에서 등록이 사라짐:** 팀/클라우드 동기화가 로컬 MCP를 덮어쓴 이슈([#59368](https://github.com/anthropics/claude-code/issues/59368), 수정됨). 동기화를 끄거나 **CLI + 프로젝트 `.mcp.json`** 사용.
|
|
107
|
+
- **연결 검증:** `get_onboarding_status` 호출(Codex는 `/mcp`). 빠른 점검: `npx -y careermate doctor`(Node·데이터 폴더·DB 상태).
|
|
108
|
+
- **자세한 복구**(포트 충돌·DB 마이그레이션·완전 초기화): 루트 [`/INSTALL.md`](../INSTALL.md) 9단계.
|
|
109
|
+
|
|
110
|
+
---
|
|
59
111
|
|
|
60
|
-
각 모드의 **영구 지침 파일**: Claude Code → [/CLAUDE.md](../CLAUDE.md), Codex → [/AGENTS.md](../AGENTS.md).
|
|
112
|
+
각 모드의 **영구 지침 파일**: Claude Code → [/CLAUDE.md](../CLAUDE.md), Codex → [/AGENTS.md](../AGENTS.md). 사람용 설치 → https://careermate.life · 사용 흐름 → [docs/START_WORKFLOW.md](./START_WORKFLOW.md).
|
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
|
@@ -7,8 +7,8 @@ CareerMate는 내부에 LLM을 두지 않는다. 분석과 작성은 사용자
|
|
|
7
7
|
관련 문서:
|
|
8
8
|
|
|
9
9
|
- 연결 직후 AI가 따라가는 시작 런북은 [시작 런북](./START_WORKFLOW.md)을 참고한다.
|
|
10
|
-
- 설치·연결 절차는 [설치
|
|
11
|
-
- MCP 도구
|
|
10
|
+
- 설치·연결 절차는 [설치 런북](../INSTALL.md)(또는 [지원 앱·설치 레퍼런스](./SUPPORTED_AI_APPS.md))을 참고한다.
|
|
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",
|
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
"apps",
|
|
30
30
|
"packages",
|
|
31
31
|
"scripts",
|
|
32
|
-
"install-page",
|
|
33
32
|
"docs",
|
|
34
33
|
"tsconfig.json",
|
|
35
34
|
"README.md",
|
|
@@ -48,6 +47,8 @@
|
|
|
48
47
|
"mcp": "node --no-warnings --experimental-sqlite --import tsx apps/mcp/src/index.ts",
|
|
49
48
|
"migrate": "node --no-warnings --experimental-sqlite --import tsx scripts/migrate.ts",
|
|
50
49
|
"seed": "node --no-warnings --experimental-sqlite --import tsx scripts/seed.ts",
|
|
50
|
+
"shots:seed": "node --no-warnings --experimental-sqlite --import tsx scripts/shots-seed.ts",
|
|
51
|
+
"shots": "node --no-warnings --experimental-sqlite --import tsx scripts/shots.ts",
|
|
51
52
|
"doctor": "node --no-warnings --experimental-sqlite --import tsx scripts/doctor.ts",
|
|
52
53
|
"test": "node --no-warnings scripts/run.mjs scripts/test.ts \"TEST_VERDICT PASS\"",
|
|
53
54
|
"test:ui": "node --no-warnings scripts/run.mjs scripts/ui-smoke.ts \"UI_VERDICT PASS\"",
|
|
@@ -55,13 +56,17 @@
|
|
|
55
56
|
},
|
|
56
57
|
"dependencies": {
|
|
57
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",
|
|
58
63
|
"tsx": "^4.19.2",
|
|
59
64
|
"zod": "^3.24.1"
|
|
60
65
|
},
|
|
61
66
|
"devDependencies": {
|
|
62
67
|
"@playwright/test": "^1.60.0",
|
|
63
68
|
"@types/node": "^22.10.0",
|
|
64
|
-
"esbuild": "^0.
|
|
69
|
+
"esbuild": "^0.28.1",
|
|
65
70
|
"playwright": "^1.60.0",
|
|
66
71
|
"typescript": "^5.7.2"
|
|
67
72
|
}
|
|
@@ -20,6 +20,12 @@ import {
|
|
|
20
20
|
type DocumentRecord,
|
|
21
21
|
type ProfileInput,
|
|
22
22
|
type ProfileRecord,
|
|
23
|
+
type ExperienceInput,
|
|
24
|
+
type ExperienceRecord,
|
|
25
|
+
type ProjectInput,
|
|
26
|
+
type ProjectRecord,
|
|
27
|
+
type SkillInput,
|
|
28
|
+
type SkillRecord,
|
|
23
29
|
APPLICATION_STATUS_LABELS,
|
|
24
30
|
INTERVIEW_UNLOCK_STATUSES,
|
|
25
31
|
} from '@careermate/shared';
|
|
@@ -31,6 +37,9 @@ import {
|
|
|
31
37
|
interviewRepo,
|
|
32
38
|
documentRepo,
|
|
33
39
|
profileRepo,
|
|
40
|
+
experienceRepo,
|
|
41
|
+
projectRepo,
|
|
42
|
+
skillRepo,
|
|
34
43
|
activityRepo,
|
|
35
44
|
} from '@careermate/db';
|
|
36
45
|
|
|
@@ -50,6 +59,30 @@ export function addResume(input: Omit<DocumentInput, 'kind'> & { kind?: Document
|
|
|
50
59
|
return doc;
|
|
51
60
|
}
|
|
52
61
|
|
|
62
|
+
/* ----------------------------------------------- Experiences / Projects / Skills */
|
|
63
|
+
|
|
64
|
+
// These structured-profile entities have no dedicated activity type; the dashboard
|
|
65
|
+
// logs them under 'profile_updated' (see apps/web routes), so the MCP path matches.
|
|
66
|
+
// Summaries carry only company/project/skill names — never résumé body text.
|
|
67
|
+
|
|
68
|
+
export function addExperience(input: ExperienceInput): ExperienceRecord {
|
|
69
|
+
const exp = experienceRepo.add(input);
|
|
70
|
+
activityRepo.log('profile_updated', `경력(${exp.company})을 추가했습니다.`, 'experience', exp.id);
|
|
71
|
+
return exp;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function addProject(input: ProjectInput): ProjectRecord {
|
|
75
|
+
const prj = projectRepo.add(input);
|
|
76
|
+
activityRepo.log('profile_updated', `프로젝트(${prj.name})를 추가했습니다.`, 'project', prj.id);
|
|
77
|
+
return prj;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function addSkill(input: SkillInput): SkillRecord {
|
|
81
|
+
const skill = skillRepo.add(input);
|
|
82
|
+
activityRepo.log('profile_updated', `기술(${skill.name})을 추가했습니다.`, 'skill', skill.id);
|
|
83
|
+
return skill;
|
|
84
|
+
}
|
|
85
|
+
|
|
53
86
|
/* -------------------------------------------------------------------- Jobs */
|
|
54
87
|
|
|
55
88
|
/** Save (or update) a posting and make sure an application row tracks it. */
|
|
@@ -139,7 +172,15 @@ export function updateApplicationStatus(
|
|
|
139
172
|
export function saveInterviewPrep(input: InterviewPrepInput): InterviewPrepRecord {
|
|
140
173
|
const job = jobRepo.get(input.job_id);
|
|
141
174
|
if (!job) throw new Error(`공고를 찾을 수 없습니다: ${input.job_id}`);
|
|
142
|
-
|
|
175
|
+
// Spec gate: interview prep is only allowed once the application has reached
|
|
176
|
+
// 서류 합격(document_passed) or beyond. Enforce it server-side, not just in prompts.
|
|
177
|
+
const app = applicationRepo.getByJob(job.id);
|
|
178
|
+
if (!app || !INTERVIEW_UNLOCK_STATUSES.includes(app.status)) {
|
|
179
|
+
const current = app ? APPLICATION_STATUS_LABELS[app.status] : '미지원';
|
|
180
|
+
throw new Error(
|
|
181
|
+
`면접 준비는 '서류 합격' 이상 상태에서만 저장할 수 있습니다. 현재 상태: '${current}'. 먼저 update_application_status로 상태를 올린 뒤 다시 시도하세요.`,
|
|
182
|
+
);
|
|
183
|
+
}
|
|
143
184
|
const prep = interviewRepo.save(input);
|
|
144
185
|
activityRepo.log('interview_prep_saved', `${job.company} · ${job.position} 면접 준비 자료를 저장했습니다.`, 'interview_prep', prep.id);
|
|
145
186
|
return prep;
|
|
@@ -28,6 +28,36 @@ export function closeDb(): void {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
let _inTx = false;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Run `fn` inside a single transaction so multi-statement writes are atomic:
|
|
35
|
+
* any thrown error rolls back every statement instead of leaving a half-written
|
|
36
|
+
* row. node:sqlite has no transaction() helper, so we drive BEGIN/COMMIT/ROLLBACK
|
|
37
|
+
* directly. Re-entrant: a nested tx() call joins the outer transaction (SQLite
|
|
38
|
+
* has no nested transactions), so only the outermost call commits or rolls back.
|
|
39
|
+
*/
|
|
40
|
+
export function tx<T>(fn: () => T): T {
|
|
41
|
+
if (_inTx) return fn();
|
|
42
|
+
const db = getDb();
|
|
43
|
+
db.exec('BEGIN');
|
|
44
|
+
_inTx = true;
|
|
45
|
+
try {
|
|
46
|
+
const result = fn();
|
|
47
|
+
db.exec('COMMIT');
|
|
48
|
+
return result;
|
|
49
|
+
} catch (err) {
|
|
50
|
+
try {
|
|
51
|
+
db.exec('ROLLBACK');
|
|
52
|
+
} catch {
|
|
53
|
+
/* ignore rollback failure; surface the original error */
|
|
54
|
+
}
|
|
55
|
+
throw err;
|
|
56
|
+
} finally {
|
|
57
|
+
_inTx = false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
31
61
|
/** Helpers for the JSON-text columns. */
|
|
32
62
|
export function toJson(value: unknown): string {
|
|
33
63
|
return JSON.stringify(value ?? null);
|
package/packages/db/src/index.ts
CHANGED
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 });
|