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.
Files changed (124) hide show
  1. package/README.md +256 -0
  2. package/THIRD_PARTY_NOTICES.md +40 -0
  3. package/apps/mcp/src/index.ts +66 -0
  4. package/apps/web/DESIGN_GUIDE.md +105 -0
  5. package/apps/web/UI_CONTRACT.md +44 -0
  6. package/apps/web/public/app.js +118 -0
  7. package/apps/web/public/fonts/PretendardVariable.woff2 +0 -0
  8. package/apps/web/public/index.html +41 -0
  9. package/apps/web/public/lib.js +282 -0
  10. package/apps/web/public/pages/applications.js +98 -0
  11. package/apps/web/public/pages/documents.js +446 -0
  12. package/apps/web/public/pages/home.js +263 -0
  13. package/apps/web/public/pages/interview.js +230 -0
  14. package/apps/web/public/pages/jobs.js +494 -0
  15. package/apps/web/public/pages/profile.js +576 -0
  16. package/apps/web/public/pages/settings.js +233 -0
  17. package/apps/web/public/styles.css +426 -0
  18. package/apps/web/src/exports.ts +68 -0
  19. package/apps/web/src/http.ts +180 -0
  20. package/apps/web/src/index.ts +49 -0
  21. package/apps/web/src/info.ts +50 -0
  22. package/apps/web/src/routes.ts +350 -0
  23. package/apps/web/src/security.ts +102 -0
  24. package/apps/web/src/server.ts +141 -0
  25. package/apps/web/src/settings.ts +88 -0
  26. package/bin/careermate.mjs +74 -0
  27. package/dist/careermate.mcpb +0 -0
  28. package/dist/install-page/index.html +474 -0
  29. package/dist/install-page/style.css +391 -0
  30. package/dist/install-page/vercel.json +20 -0
  31. package/dist/mcp-smoke.err +3 -0
  32. package/dist/mcp.mjs +23704 -0
  33. package/dist/mcpb-stage/README.md +219 -0
  34. package/dist/mcpb-stage/dist/install-page/index.html +434 -0
  35. package/dist/mcpb-stage/dist/install-page/style.css +407 -0
  36. package/dist/mcpb-stage/dist/install-page/vercel.json +20 -0
  37. package/dist/mcpb-stage/dist/mcp.mjs +23704 -0
  38. package/dist/mcpb-stage/dist/public/app.js +118 -0
  39. package/dist/mcpb-stage/dist/public/fonts/PretendardVariable.woff2 +0 -0
  40. package/dist/mcpb-stage/dist/public/index.html +41 -0
  41. package/dist/mcpb-stage/dist/public/lib.js +282 -0
  42. package/dist/mcpb-stage/dist/public/pages/applications.js +98 -0
  43. package/dist/mcpb-stage/dist/public/pages/documents.js +446 -0
  44. package/dist/mcpb-stage/dist/public/pages/home.js +263 -0
  45. package/dist/mcpb-stage/dist/public/pages/interview.js +230 -0
  46. package/dist/mcpb-stage/dist/public/pages/jobs.js +494 -0
  47. package/dist/mcpb-stage/dist/public/pages/profile.js +576 -0
  48. package/dist/mcpb-stage/dist/public/pages/settings.js +233 -0
  49. package/dist/mcpb-stage/dist/public/styles.css +420 -0
  50. package/dist/mcpb-stage/dist/web.mjs +7240 -0
  51. package/dist/mcpb-stage/manifest.json +40 -0
  52. package/dist/public/app.js +118 -0
  53. package/dist/public/fonts/PretendardVariable.woff2 +0 -0
  54. package/dist/public/index.html +41 -0
  55. package/dist/public/lib.js +282 -0
  56. package/dist/public/pages/applications.js +98 -0
  57. package/dist/public/pages/documents.js +446 -0
  58. package/dist/public/pages/home.js +263 -0
  59. package/dist/public/pages/interview.js +230 -0
  60. package/dist/public/pages/jobs.js +494 -0
  61. package/dist/public/pages/profile.js +576 -0
  62. package/dist/public/pages/settings.js +233 -0
  63. package/dist/public/styles.css +426 -0
  64. package/dist/web.mjs +7240 -0
  65. package/docs/ARCHITECTURE.md +208 -0
  66. package/docs/CHANGES_V1.md +103 -0
  67. package/docs/DATA_MODEL.md +460 -0
  68. package/docs/DECISIONS.md +277 -0
  69. package/docs/DEMO.md +242 -0
  70. package/docs/INSTALL.md +148 -0
  71. package/docs/INSTALL_AND_USAGE.md +99 -0
  72. package/docs/MCP_TOOLS.md +233 -0
  73. package/docs/ROADMAP.md +134 -0
  74. package/docs/START_WORKFLOW.md +125 -0
  75. package/docs/SUPPORTED_AI_APPS.md +60 -0
  76. package/docs/TODO.md +57 -0
  77. package/docs/UX_NOTES.md +247 -0
  78. package/docs/WORKFLOWS.md +200 -0
  79. package/install-page/index.html +474 -0
  80. package/install-page/style.css +391 -0
  81. package/install-page/vercel.json +20 -0
  82. package/package.json +68 -0
  83. package/packages/core/src/context.ts +74 -0
  84. package/packages/core/src/index.ts +8 -0
  85. package/packages/core/src/onboarding.ts +81 -0
  86. package/packages/core/src/services.ts +146 -0
  87. package/packages/core/src/summary.ts +104 -0
  88. package/packages/db/src/connection.ts +46 -0
  89. package/packages/db/src/index.ts +22 -0
  90. package/packages/db/src/paths.ts +41 -0
  91. package/packages/db/src/repositories.ts +828 -0
  92. package/packages/db/src/runtime.ts +58 -0
  93. package/packages/db/src/schema.ts +189 -0
  94. package/packages/exporters/src/html.ts +113 -0
  95. package/packages/exporters/src/index.ts +364 -0
  96. package/packages/exporters/src/markdown.ts +178 -0
  97. package/packages/mcp-tools/src/bridge.ts +83 -0
  98. package/packages/mcp-tools/src/index.ts +8 -0
  99. package/packages/mcp-tools/src/result.ts +49 -0
  100. package/packages/mcp-tools/src/tools.ts +455 -0
  101. package/packages/parsers/src/html.ts +86 -0
  102. package/packages/parsers/src/index.ts +228 -0
  103. package/packages/parsers/src/keywords.ts +151 -0
  104. package/packages/prompts/src/humanize.ts +59 -0
  105. package/packages/prompts/src/index.ts +82 -0
  106. package/packages/prompts/src/install.ts +43 -0
  107. package/packages/prompts/src/onboarding.ts +35 -0
  108. package/packages/prompts/src/system.ts +53 -0
  109. package/packages/shared/src/enums.ts +103 -0
  110. package/packages/shared/src/index.ts +18 -0
  111. package/packages/shared/src/schemas.ts +398 -0
  112. package/packages/workflows/src/definitions.ts +107 -0
  113. package/packages/workflows/src/index.ts +39 -0
  114. package/scripts/build-dist.mjs +62 -0
  115. package/scripts/build-mcpb.mjs +70 -0
  116. package/scripts/doctor.ts +81 -0
  117. package/scripts/init.ts +342 -0
  118. package/scripts/mcp-probe.ts +55 -0
  119. package/scripts/migrate.ts +6 -0
  120. package/scripts/run.mjs +33 -0
  121. package/scripts/seed.ts +129 -0
  122. package/scripts/test.ts +117 -0
  123. package/scripts/ui-smoke.ts +73 -0
  124. package/tsconfig.json +29 -0
@@ -0,0 +1,434 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ko" data-theme="">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>CareerMate — 내 AI를 커리어 비서로</title>
7
+ <meta name="description" content="프롬프트 하나를 복사해서 ChatGPT·Claude·Gemini에 붙여넣으면 끝. CareerMate가 당신의 커리어 데이터를 이 컴퓨터에만 저장하고 MCP로 당신의 AI에게 연결합니다." />
8
+ <link rel="stylesheet" href="style.css" />
9
+ </head>
10
+ <body>
11
+ <!-- ============================================================ nav -->
12
+ <nav class="nav">
13
+ <a class="nav__brand" href="#top">
14
+ <span class="nav__logo" aria-hidden="true">C</span>
15
+ <span class="nav__name">CareerMate</span>
16
+ </a>
17
+ <div class="nav__links">
18
+ <a href="#how">동작 방식</a>
19
+ <a href="#features">기능</a>
20
+ <a href="#manual">수동 설치</a>
21
+ <a href="#privacy">개인정보</a>
22
+ </div>
23
+ <div class="nav__actions">
24
+ <button class="theme-toggle" id="theme-toggle" type="button" aria-label="다크 모드 전환" title="다크 모드 전환">
25
+ <svg class="i-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
26
+ <svg class="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
27
+ </button>
28
+ <a class="btn btn--ghost" href="#top">시작하기</a>
29
+ </div>
30
+ </nav>
31
+
32
+ <main id="top">
33
+
34
+ <!-- ========================================================== hero -->
35
+ <header class="hero">
36
+ <span class="pill-badge">
37
+ <span class="pill-badge__dot" aria-hidden="true"></span>
38
+ 로컬 전용 · 오픈소스 · 설치 5분
39
+ </span>
40
+ <h1 class="hero__title">
41
+ 내가 쓰던 <span class="accent">AI</span>를<br />
42
+ 나만의 <span class="accent">커리어 비서</span>로
43
+ </h1>
44
+ <p class="hero__lede">
45
+ <strong>프롬프트 한 줄</strong>이면 연결 끝.<br />
46
+ 이력서·자기소개서·지원 현황을 <strong>이 컴퓨터 안에서</strong> 관리하세요.
47
+ </p>
48
+
49
+ <!-- paste one short line into your AI; it reads the install guide and connects -->
50
+ <section class="connect" aria-label="CareerMate 연결">
51
+ <p class="connect__lead">평소 쓰는 AI에게 아래 한 줄을 붙여넣으세요. AI가 설치 안내를 읽고 연결까지 진행합니다.</p>
52
+ <div class="oneclick__pills" role="group" aria-label="AI 선택">
53
+ <button class="ai-pill is-active" type="button" aria-pressed="true" data-prompt="claude">Claude</button>
54
+ <button class="ai-pill" type="button" aria-pressed="false" data-prompt="chatgpt">ChatGPT</button>
55
+ <button class="ai-pill" type="button" aria-pressed="false" data-prompt="gemini">Gemini</button>
56
+ <button class="ai-pill" type="button" aria-pressed="false" data-prompt="generic">기타 AI</button>
57
+ </div>
58
+ <div class="oneclick__box">
59
+ <pre class="oneclick__text" id="prompt-text" tabindex="0"></pre>
60
+ <div class="oneclick__fade" aria-hidden="true"></div>
61
+ <button class="btn btn--primary btn--copy-lg" id="copy-prompt" type="button">
62
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
63
+ <span class="copy-label">프롬프트 복사</span>
64
+ </button>
65
+ </div>
66
+ <p class="connect__note">Claude Desktop · Cursor · Claude Code 등 <strong>내 컴퓨터에 접근할 수 있는 AI</strong>에서 동작합니다. 직접 설정하려면 <a href="#manual">수동 설치</a>를 이용하세요.</p>
67
+ </section>
68
+
69
+ <p class="oneclick__hint">
70
+ 이미 설치를 마쳤다면 로컬 대시보드 <code>http://127.0.0.1:4319</code>에서 내 데이터를 확인하세요.<br />
71
+ 손으로 직접 설치하고 싶다면 <a href="#manual">수동 설치 가이드</a>를 참고하세요.
72
+ </p>
73
+ </header>
74
+
75
+ <!-- ========================================================== how -->
76
+ <section id="how" class="section">
77
+ <div class="section__head">
78
+ <span class="eyebrow">동작 방식</span>
79
+ <h2>당신의 AI가 <span class="accent">두뇌</span>,<br />CareerMate는 <span class="accent">커리어 서랍장</span></h2>
80
+ <p>CareerMate 안에는 AI가 들어 있지 않습니다.<br />생각하는 일은 당신의 AI가, 데이터 보관은 CareerMate가 맡습니다.</p>
81
+ </div>
82
+ <div class="cards-3">
83
+ <article class="feature">
84
+ <div class="feature__icon">
85
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 5a3 3 0 1 0-5.99.13 3 3 0 0 0-1.5 5.5A3 3 0 0 0 6 16.5a3 3 0 0 0 6 .5V5z"/><path d="M12 5a3 3 0 1 1 5.99.13 3 3 0 0 1 1.5 5.5A3 3 0 0 1 18 16.5a3 3 0 0 1-6 .5V5z"/></svg>
86
+ </div>
87
+ <h3>1. 당신의 AI와 대화</h3>
88
+ <p>평소 쓰던 ChatGPT·Claude·Gemini에게 말로 시키면 됩니다.<br />분석·글쓰기 같은 “생각”은 전부 당신의 AI가 합니다.</p>
89
+ </article>
90
+ <article class="feature">
91
+ <div class="feature__icon">
92
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 12h6"/><path d="M9 12a3 3 0 0 1-3-3V7a3 3 0 0 1 3-3"/><path d="M15 12a3 3 0 0 0 3 3v2a3 3 0 0 1-3 3"/></svg>
93
+ </div>
94
+ <h3>2. MCP로 연결</h3>
95
+ <p>표준 프로토콜 MCP가 AI와 CareerMate를 잇는 “USB-C” 역할을 합니다.<br />AI가 24개 도구로 당신의 커리어 DB를 읽고 씁니다.</p>
96
+ </article>
97
+ <article class="feature">
98
+ <div class="feature__icon">
99
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="6" rx="1.5"/><rect x="3" y="14" width="18" height="6" rx="1.5"/><path d="M7 7h.01M7 17h.01"/></svg>
100
+ </div>
101
+ <h3>3. 이 컴퓨터에 저장</h3>
102
+ <p>모든 데이터는 로컬 SQLite(<code>~/.careermate</code>)에만 저장됩니다.<br />외부 서버로 전송하지 않습니다. 대시보드로 눈으로 확인하세요.</p>
103
+ </article>
104
+ </div>
105
+ </section>
106
+
107
+ <!-- ====================================================== features -->
108
+ <section id="features" class="section">
109
+ <div class="section__head">
110
+ <span class="eyebrow">할 수 있는 일</span>
111
+ <h2>지원 한 건을 <span class="accent">끝까지</span> 함께 처리</h2>
112
+ <p>프로필 정리부터 면접 준비까지, AI와 대화만으로 이어집니다.</p>
113
+ </div>
114
+ <div class="checks">
115
+ <div class="check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg><div><strong>프로필·이력서 구조화</strong><span>업로드한 파일을 읽어 경력·프로젝트·스킬로 정리·보관.</span></div></div>
116
+ <div class="check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg><div><strong>채용공고 파싱·저장</strong><span>붙여넣은 공고 텍스트에서 회사·직무·자격요건을 추출.</span></div></div>
117
+ <div class="check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg><div><strong>적합도(핏) 분석</strong><span>내 경력과 공고를 비교해 강점·보완점·매칭 키워드 도출.</span></div></div>
118
+ <div class="check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg><div><strong>맞춤 자소서 + 버전 관리</strong><span>공고별 자기소개서를 버전으로 쌓고 비교, 파일로 내보내기.</span></div></div>
119
+ <div class="check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg><div><strong>지원 상태 8단계 관리</strong><span>작성 중 → 지원 → 서류 합격 → 면접 → 최종까지 칸반으로.</span></div></div>
120
+ <div class="check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M20 6 9 17l-5-5"/></svg><div><strong>면접 준비</strong><span>서류 합격 시 예상 질문·STAR 답변·1분 자기소개 초안 생성.</span></div></div>
121
+ </div>
122
+ </section>
123
+
124
+ <!-- ======================================================== manual -->
125
+ <section id="manual" class="section">
126
+ <div class="section__head">
127
+ <span class="eyebrow">수동 설치</span>
128
+ <h2>직접 손으로 설치하기</h2>
129
+ <p>AI에게 맡기지 않고 단계별로 설치하고 싶을 때. 개발 경험이 없어도 그대로 따라 하면 됩니다.</p>
130
+ </div>
131
+
132
+ <div class="note">
133
+ <strong>준비물은 Node.js 22.5 이상</strong> 하나뿐입니다. 내장 SQLite를 쓰므로 별도 컴파일러·빌드 도구가 필요 없습니다.
134
+ 터미널(Windows는 PowerShell)에서 <code>node --version</code>으로 확인하고, 낮으면
135
+ <a href="https://nodejs.org/" rel="noopener">nodejs.org</a>에서 LTS를 설치하세요.
136
+ </div>
137
+
138
+ <ol class="steps">
139
+ <li class="step">
140
+ <div class="step__no">1</div>
141
+ <div class="step__body">
142
+ <h3>설치</h3>
143
+ <p>CareerMate 폴더에서 의존성을 한 번만 설치합니다. (빌드 단계 없음)</p>
144
+ <div class="code">
145
+ <span class="code__label">프로젝트 폴더에서</span>
146
+ <button class="copy-btn" type="button" data-copy-target="code-install">복사</button>
147
+ <pre id="code-install">npm install</pre>
148
+ </div>
149
+ </div>
150
+ </li>
151
+ <li class="step">
152
+ <div class="step__no">2</div>
153
+ <div class="step__body">
154
+ <h3>대시보드 실행</h3>
155
+ <p>실행하면 대시보드가 켜지고 브라우저가 자동으로 열립니다. 기본 주소는 <code>http://127.0.0.1:4319</code>, 포트가 사용 중이면 다음 빈 포트로 넘어갑니다. 종료는 <kbd>Ctrl</kbd>+<kbd>C</kbd>.</p>
156
+ <div class="code">
157
+ <span class="code__label">대시보드 시작</span>
158
+ <button class="copy-btn" type="button" data-copy-target="code-start">복사</button>
159
+ <pre id="code-start">npm start</pre>
160
+ </div>
161
+ </div>
162
+ </li>
163
+ <li class="step">
164
+ <div class="step__no">3</div>
165
+ <div class="step__body">
166
+ <h3>AI에 MCP 서버 연결</h3>
167
+ <p>AI 클라이언트 설정 파일(JSON)에 항목 하나를 추가합니다. <code>C:\path\to\careermate</code> 부분을
168
+ <strong>실제 CareerMate 폴더 경로</strong>(<code>apps\mcp\src\index.ts</code>까지)로 바꾸세요.
169
+ Windows 경로의 역슬래시는 JSON 안에서 <code>\\</code>처럼 두 번 씁니다.</p>
170
+
171
+ <div class="tabs" role="tablist" aria-label="AI 클라이언트 선택">
172
+ <button class="tab is-active" id="tab-claude" role="tab" aria-controls="panel-claude" aria-selected="true">Claude Desktop</button>
173
+ <button class="tab" id="tab-chatgpt" role="tab" aria-controls="panel-chatgpt" aria-selected="false" tabindex="-1">ChatGPT</button>
174
+ <button class="tab" id="tab-cursor" role="tab" aria-controls="panel-cursor" aria-selected="false" tabindex="-1">Cursor / 기타</button>
175
+ </div>
176
+
177
+ <div class="panel" id="panel-claude" role="tabpanel" aria-labelledby="tab-claude" data-active="true">
178
+ <p class="path-note">Windows: <code>%APPDATA%\Claude\claude_desktop_config.json</code> · macOS: <code>~/Library/Application Support/Claude/claude_desktop_config.json</code><br />(Settings → Developer → Edit Config 로도 열 수 있습니다. 수정 후 앱을 완전히 종료했다 다시 켜세요.)</p>
179
+ <div class="code">
180
+ <span class="code__label">claude_desktop_config.json</span>
181
+ <button class="copy-btn" type="button" data-copy-target="code-cfg-claude">복사</button>
182
+ <pre id="code-cfg-claude">{
183
+ "mcpServers": {
184
+ "careermate": {
185
+ "command": "node",
186
+ "args": ["--no-warnings", "--import", "tsx", "C:\\path\\to\\careermate\\apps\\mcp\\src\\index.ts"]
187
+ }
188
+ }
189
+ }</pre>
190
+ </div>
191
+ </div>
192
+
193
+ <div class="panel" id="panel-chatgpt" role="tabpanel" aria-labelledby="tab-chatgpt" data-active="false" hidden>
194
+ <p class="path-note">Settings에서 <em>Connectors</em> 또는 <em>Developer / MCP</em> 항목을 찾아 로컬 MCP 서버를 추가하세요. 메뉴가 없으면 앱을 최신으로 업데이트하거나 Claude Desktop·Cursor로 연결하는 편이 확실합니다.</p>
195
+ <div class="code">
196
+ <span class="code__label">MCP 서버 설정 (command / args)</span>
197
+ <button class="copy-btn" type="button" data-copy-target="code-cfg-chatgpt">복사</button>
198
+ <pre id="code-cfg-chatgpt">{
199
+ "mcpServers": {
200
+ "careermate": {
201
+ "command": "node",
202
+ "args": ["--no-warnings", "--import", "tsx", "C:\\path\\to\\careermate\\apps\\mcp\\src\\index.ts"]
203
+ }
204
+ }
205
+ }</pre>
206
+ </div>
207
+ </div>
208
+
209
+ <div class="panel" id="panel-cursor" role="tabpanel" aria-labelledby="tab-cursor" data-active="false" hidden>
210
+ <p class="path-note">프로젝트별: <code>.cursor/mcp.json</code> · 전역: <code>~/.cursor/mcp.json</code> — Cursor·Cline·Windsurf 등 대부분 같은 형식입니다. 추가 후 클라이언트를 다시 시작하세요.</p>
211
+ <div class="code">
212
+ <span class="code__label">.cursor/mcp.json</span>
213
+ <button class="copy-btn" type="button" data-copy-target="code-cfg-cursor">복사</button>
214
+ <pre id="code-cfg-cursor">{
215
+ "mcpServers": {
216
+ "careermate": {
217
+ "command": "node",
218
+ "args": ["--no-warnings", "--import", "tsx", "C:\\path\\to\\careermate\\apps\\mcp\\src\\index.ts"]
219
+ }
220
+ }
221
+ }</pre>
222
+ </div>
223
+ </div>
224
+
225
+ <p class="step__tip">연결 후 AI에게 <strong>“<code>get_onboarding_status</code>를 호출해서 연결됐는지 확인해줘”</strong>라고 말해 보세요. 결과가 돌아오면 정상입니다.</p>
226
+ </div>
227
+ </li>
228
+ </ol>
229
+
230
+ <details class="acc">
231
+ <summary>문제가 생겼을 때 <span class="acc__hint">포트 충돌 · MCP 연결 실패 · 데이터 위치 · 초기화</span></summary>
232
+ <div class="acc__body">
233
+ <div class="trouble">
234
+ <div class="trouble__item">
235
+ <h4>포트가 이미 사용 중</h4>
236
+ <p>4319가 막히면 자동으로 다음 빈 포트로 옮겨갑니다. 터미널에 출력된 실제 주소를 사용하거나, <code>CAREERMATE_PORT</code> 환경변수로 고정하세요.</p>
237
+ </div>
238
+ <div class="trouble__item">
239
+ <h4>AI에 MCP가 연결되지 않음</h4>
240
+ <p>① 설정 JSON 경로가 <code>apps/mcp/src/index.ts</code>까지 정확한지(Windows는 <code>\\</code>) ② <code>node --version</code>이 22.5 이상인지 ③ 클라이언트를 <strong>완전히 종료 후 재시작</strong> ④ <code>npm install</code>이 끝났는지 확인하세요.</p>
241
+ </div>
242
+ <div class="trouble__item">
243
+ <h4>상태 점검</h4>
244
+ <p>프로젝트 폴더에서 진단을 실행하면 Node 버전·데이터 폴더·DB 상태를 점검합니다.</p>
245
+ <div class="code">
246
+ <button class="copy-btn" type="button" data-copy-target="code-doctor">복사</button>
247
+ <pre id="code-doctor">npm run doctor</pre>
248
+ </div>
249
+ </div>
250
+ <div class="trouble__item">
251
+ <h4>데이터 위치 / 초기화</h4>
252
+ <p>기본 위치는 <code>~/.careermate/careermate.sqlite</code>(Windows: <code>%USERPROFILE%\.careermate</code>). <code>CAREERMATE_DATA_DIR</code>로 변경 가능. <code>npm run migrate</code>는 DB를 준비/업그레이드(반복 실행 안전)하고, 완전 초기화는 <code>careermate.sqlite</code> 삭제 후 재실행(백업 권장)입니다.</p>
253
+ </div>
254
+ </div>
255
+ </div>
256
+ </details>
257
+ </section>
258
+
259
+ <!-- ======================================================= privacy -->
260
+ <section id="privacy" class="section">
261
+ <div class="privacy-card">
262
+ <div class="privacy-card__icon" aria-hidden="true">
263
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="M9 12l2 2 4-4"/></svg>
264
+ </div>
265
+ <div class="privacy-card__text">
266
+ <h2>당신의 데이터는 당신 컴퓨터를 떠나지 않습니다</h2>
267
+ <p>대시보드 서버는 <code>127.0.0.1</code>(이 컴퓨터)에만 바인딩되어 외부에서 접근할 수 없고,<br />MCP 서버는 네트워크 호출을 하지 않습니다. 분석·글쓰기는 당신이 직접 고른 AI가 수행합니다.</p>
268
+ <ul class="privacy-card__list">
269
+ <li><strong>저장 위치</strong> <code>~/.careermate/careermate.sqlite</code></li>
270
+ <li><strong>내보내기</strong> 자기소개서 등을 <code>~/.careermate/exports</code>로</li>
271
+ <li><strong>삭제·관리</strong> 대시보드 <strong>설정(Settings)</strong>에서 직접</li>
272
+ </ul>
273
+ </div>
274
+ </div>
275
+ </section>
276
+
277
+ </main>
278
+
279
+ <footer class="foot">
280
+ <div class="foot__brand"><span class="nav__logo" aria-hidden="true">C</span> CareerMate</div>
281
+ <p>로컬에서 동작하는 MCP 기반 커리어 관리 도구. 모든 데이터는 이 컴퓨터에만 저장됩니다.</p>
282
+ <div class="foot__links">
283
+ <a href="#top">맨 위로</a>
284
+ <a href="#features">기능</a>
285
+ <a href="#manual">설치</a>
286
+ </div>
287
+ </footer>
288
+
289
+ <div class="sr-only" role="status" aria-live="polite" id="copy-status"></div>
290
+
291
+ <!-- prompt sources (hidden; one-click hero reads from here) -->
292
+ <div class="sr-only" aria-hidden="true">
293
+ <pre id="src-claude">Claude야, 내 컴퓨터에 CareerMate를 설치하고 연결해줘. CareerMate 설치 안내 페이지(또는 INSTALL.md)를 그대로 따라 설치·연결한 다음, `get_onboarding_status`로 연결을 확인하고 온보딩을 시작해줘. 매 단계 쉬운 한국어로 알려주고, 막히면 추측하지 말고 나에게 물어봐줘.</pre>
294
+ <pre id="src-chatgpt">ChatGPT야, 내 컴퓨터에 CareerMate를 설치하고 연결해줘. CareerMate 설치 안내 페이지(또는 INSTALL.md)를 그대로 따라 설치·연결한 다음, `get_onboarding_status`로 연결을 확인하고 온보딩을 시작해줘. 매 단계 쉬운 한국어로 설명하고, 확실하지 않으면 지어내지 말고 나에게 물어봐줘.</pre>
295
+ <pre id="src-gemini">Gemini야, 내 컴퓨터에 CareerMate를 설치하고 연결해줘. CareerMate는 내 커리어 데이터를 내 컴퓨터에 저장하고 MCP로 네가 읽고 쓸 수 있게 해주는 도구야. 설치 안내 페이지(또는 INSTALL.md)를 그대로 따라 설치·연결한 뒤, `get_onboarding_status`로 연결을 확인하고 온보딩을 시작해줘. 매 단계 쉬운 한국어로 설명하고, 불확실하면 지어내지 말고 나에게 확인해줘.</pre>
296
+ <pre id="src-generic">안녕, 내 컴퓨터에 CareerMate를 설치하고 연결해줘. CareerMate는 내 커리어 데이터를 내 컴퓨터에 저장하고 MCP로 네(내 AI)가 읽고 쓸 수 있게 해주는 도구야. 설치 안내(이 페이지 또는 INSTALL.md)를 그대로 따라 설치·연결한 뒤, `get_onboarding_status`로 연결을 확인하고 온보딩을 시작해줘. 매 단계 쉬운 한국어로 설명하고, 불확실하면 지어내지 말고 나에게 확인해줘.</pre>
297
+ </div>
298
+
299
+ <script>
300
+ (function () {
301
+ 'use strict';
302
+ var status = document.getElementById('copy-status');
303
+
304
+ // --- generic clipboard helper ------------------------------------
305
+ function legacyCopy(text) {
306
+ var ta = document.createElement('textarea');
307
+ ta.value = text; ta.setAttribute('readonly', '');
308
+ ta.style.position = 'absolute'; ta.style.left = '-9999px';
309
+ document.body.appendChild(ta); ta.select();
310
+ var ok = false;
311
+ try { ok = document.execCommand('copy'); } catch (e) { ok = false; }
312
+ document.body.removeChild(ta);
313
+ return ok;
314
+ }
315
+ function copyText(text, onok) {
316
+ if (navigator.clipboard && navigator.clipboard.writeText) {
317
+ navigator.clipboard.writeText(text).then(onok, function () { if (legacyCopy(text)) onok(); });
318
+ } else if (legacyCopy(text)) { onok(); }
319
+ }
320
+ function announce(msg) { if (status) status.textContent = msg; }
321
+
322
+ // --- small copy buttons (code blocks) ----------------------------
323
+ function flashSmall(btn) {
324
+ var original = btn.textContent;
325
+ btn.textContent = '복사됨'; btn.classList.add('copied');
326
+ announce('클립보드에 복사되었습니다.');
327
+ window.setTimeout(function () { btn.textContent = original; btn.classList.remove('copied'); }, 1600);
328
+ }
329
+ document.querySelectorAll('.copy-btn').forEach(function (btn) {
330
+ btn.addEventListener('click', function () {
331
+ var src = document.getElementById(btn.getAttribute('data-copy-target'));
332
+ if (src) copyText(src.textContent || '', function () { flashSmall(btn); });
333
+ });
334
+ });
335
+
336
+ // --- one-click prompt: switcher + big copy -----------------------
337
+ var promptEl = document.getElementById('prompt-text');
338
+ var copyPromptBtn = document.getElementById('copy-prompt');
339
+ var pills = Array.prototype.slice.call(document.querySelectorAll('.ai-pill'));
340
+ function renderPrompt(which) {
341
+ var src = document.getElementById('src-' + which);
342
+ promptEl.textContent = src ? (src.textContent || '').trim() : '';
343
+ pills.forEach(function (p) {
344
+ var on = p.getAttribute('data-prompt') === which;
345
+ p.classList.toggle('is-active', on);
346
+ p.setAttribute('aria-pressed', on ? 'true' : 'false');
347
+ });
348
+ }
349
+ pills.forEach(function (p) {
350
+ p.addEventListener('click', function () { renderPrompt(p.getAttribute('data-prompt')); });
351
+ });
352
+ renderPrompt('claude');
353
+
354
+ if (copyPromptBtn) {
355
+ var label = copyPromptBtn.querySelector('.copy-label');
356
+ copyPromptBtn.addEventListener('click', function () {
357
+ copyText(promptEl.textContent || '', function () {
358
+ copyPromptBtn.classList.add('is-copied');
359
+ if (label) label.textContent = '복사됨! 이제 AI에 붙여넣으세요';
360
+ announce('프롬프트가 복사되었습니다. AI 대화창에 붙여넣으세요.');
361
+ window.setTimeout(function () {
362
+ copyPromptBtn.classList.remove('is-copied');
363
+ if (label) label.textContent = '프롬프트 복사';
364
+ }, 2400);
365
+ });
366
+ });
367
+ }
368
+
369
+ // --- manual-install tabs -----------------------------------------
370
+ var tablist = document.querySelector('.tabs[role="tablist"]');
371
+ if (tablist) {
372
+ var tabs = Array.prototype.slice.call(tablist.querySelectorAll('[role="tab"]'));
373
+ function selectTab(tab) {
374
+ tabs.forEach(function (t) {
375
+ var sel = t === tab;
376
+ t.setAttribute('aria-selected', sel ? 'true' : 'false');
377
+ t.classList.toggle('is-active', sel);
378
+ t.tabIndex = sel ? 0 : -1;
379
+ var panel = document.getElementById(t.getAttribute('aria-controls'));
380
+ if (panel) {
381
+ panel.setAttribute('data-active', sel ? 'true' : 'false');
382
+ if (sel) panel.removeAttribute('hidden'); else panel.setAttribute('hidden', '');
383
+ }
384
+ });
385
+ }
386
+ tabs.forEach(function (tab, i) {
387
+ tab.addEventListener('click', function () { selectTab(tab); });
388
+ tab.addEventListener('keydown', function (e) {
389
+ var idx = null;
390
+ if (e.key === 'ArrowRight') idx = (i + 1) % tabs.length;
391
+ else if (e.key === 'ArrowLeft') idx = (i - 1 + tabs.length) % tabs.length;
392
+ else if (e.key === 'Home') idx = 0;
393
+ else if (e.key === 'End') idx = tabs.length - 1;
394
+ if (idx !== null) { e.preventDefault(); tabs[idx].focus(); selectTab(tabs[idx]); }
395
+ });
396
+ });
397
+ }
398
+
399
+ // --- scroll reveal (fade-up on enter) ----------------------------
400
+ var reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
401
+ if (!reduceMotion && 'IntersectionObserver' in window) {
402
+ var revealEls = document.querySelectorAll('.section__head, .feature, .check, .step, .acc, .privacy-card');
403
+ revealEls.forEach(function (el) { el.classList.add('reveal'); });
404
+ var io = new IntersectionObserver(function (entries) {
405
+ entries.forEach(function (e) {
406
+ if (e.isIntersecting) { e.target.classList.add('is-visible'); io.unobserve(e.target); }
407
+ });
408
+ }, { threshold: 0.1, rootMargin: '0px 0px -48px 0px' });
409
+ revealEls.forEach(function (el) { io.observe(el); });
410
+ }
411
+
412
+ // --- theme toggle (persist) --------------------------------------
413
+ var root = document.documentElement;
414
+ var toggle = document.getElementById('theme-toggle');
415
+ var saved = null;
416
+ try { saved = window.localStorage.getItem('cf-theme'); } catch (e) {}
417
+ if (saved === 'dark' || saved === 'light') root.setAttribute('data-theme', saved);
418
+ function isDark() {
419
+ var t = root.getAttribute('data-theme');
420
+ if (t === 'dark') return true;
421
+ if (t === 'light') return false;
422
+ return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
423
+ }
424
+ if (toggle) {
425
+ toggle.addEventListener('click', function () {
426
+ var next = isDark() ? 'light' : 'dark';
427
+ root.setAttribute('data-theme', next);
428
+ try { window.localStorage.setItem('cf-theme', next); } catch (e) {}
429
+ });
430
+ }
431
+ })();
432
+ </script>
433
+ </body>
434
+ </html>