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.
Files changed (134) hide show
  1. package/README.md +22 -17
  2. package/THIRD_PARTY_NOTICES.md +15 -0
  3. package/apps/mcp/src/index.ts +4 -3
  4. package/apps/web/DESIGN_GUIDE.md +2 -2
  5. package/apps/web/demo/demo-shim.js +536 -0
  6. package/apps/web/demo/demo.css +75 -0
  7. package/apps/web/demo/seed.js +269 -0
  8. package/apps/web/public/app.js +34 -2
  9. package/apps/web/public/icon-180.png +0 -0
  10. package/apps/web/public/index.html +14 -3
  11. package/apps/web/public/lib.js +135 -23
  12. package/apps/web/public/pages/applications.js +18 -9
  13. package/apps/web/public/pages/documents.js +66 -50
  14. package/apps/web/public/pages/home.js +12 -11
  15. package/apps/web/public/pages/interview.js +9 -12
  16. package/apps/web/public/pages/jobs.js +39 -36
  17. package/apps/web/public/pages/profile.js +66 -86
  18. package/apps/web/public/pages/settings.js +21 -14
  19. package/apps/web/public/styles.css +105 -38
  20. package/apps/web/src/http.ts +31 -4
  21. package/apps/web/src/info.ts +9 -22
  22. package/apps/web/src/routes.ts +19 -14
  23. package/apps/web/src/server.ts +34 -29
  24. package/apps/web/src/settings.ts +35 -17
  25. package/bin/careermate.mjs +8 -5
  26. package/dist/doctor.mjs +78908 -0
  27. package/dist/install-page/.omc/state/agent-replay-55a58c0d-bb6e-43ea-8a05-4b6c93b80b34.jsonl +12 -0
  28. package/dist/install-page/.omc/state/idle-notif-cooldown.json +3 -0
  29. package/dist/install-page/.omc/state/sessions/55a58c0d-bb6e-43ea-8a05-4b6c93b80b34/mission-state.json +147 -0
  30. package/dist/install-page/.omc/state/sessions/55a58c0d-bb6e-43ea-8a05-4b6c93b80b34/pre-tool-advisory-throttle.json +22 -0
  31. package/dist/install-page/.omc/state/sessions/55a58c0d-bb6e-43ea-8a05-4b6c93b80b34/subagent-tracking-state.json +44 -0
  32. package/dist/install-page/favicon.svg +1 -0
  33. package/dist/install-page/icon-180.png +0 -0
  34. package/dist/install-page/icon-512.png +0 -0
  35. package/dist/install-page/index.html +328 -225
  36. package/dist/install-page/landing.js +54 -0
  37. package/dist/install-page/llms-install.txt +202 -0
  38. package/dist/install-page/og.png +0 -0
  39. package/dist/install-page/shots/applications-dark.png +0 -0
  40. package/dist/install-page/shots/applications-light.png +0 -0
  41. package/dist/install-page/shots/documents-dark.png +0 -0
  42. package/dist/install-page/shots/documents-light.png +0 -0
  43. package/dist/install-page/shots/home-dark.png +0 -0
  44. package/dist/install-page/shots/home-light.png +0 -0
  45. package/dist/install-page/shots/interview-dark.png +0 -0
  46. package/dist/install-page/shots/interview-light.png +0 -0
  47. package/dist/install-page/shots/jobs-dark.png +0 -0
  48. package/dist/install-page/shots/jobs-light.png +0 -0
  49. package/dist/install-page/shots/settings-dark.png +0 -0
  50. package/dist/install-page/shots/settings-light.png +0 -0
  51. package/dist/install-page/start.html +323 -0
  52. package/dist/install-page/style.css +128 -4
  53. package/dist/mcp.mjs +74225 -2007
  54. package/dist/{mcpb-stage/dist/web.mjs → migrate.mjs} +142 -2660
  55. package/dist/pdf.worker.min.mjs +28 -0
  56. package/dist/public/app.js +34 -2
  57. package/dist/public/icon-180.png +0 -0
  58. package/dist/public/index.html +14 -3
  59. package/dist/public/lib.js +135 -23
  60. package/dist/public/pages/applications.js +18 -9
  61. package/dist/public/pages/documents.js +66 -50
  62. package/dist/public/pages/home.js +12 -11
  63. package/dist/public/pages/interview.js +9 -12
  64. package/dist/public/pages/jobs.js +39 -36
  65. package/dist/public/pages/profile.js +66 -86
  66. package/dist/public/pages/settings.js +21 -14
  67. package/dist/public/styles.css +105 -38
  68. package/dist/web.mjs +39950 -369
  69. package/docs/ARCHITECTURE.md +5 -5
  70. package/docs/DECISIONS.md +1 -1
  71. package/docs/DEMO.md +6 -6
  72. package/docs/MCP_TOOLS.md +68 -9
  73. package/docs/ROADMAP.md +11 -10
  74. package/docs/RUNTIME_DISTRIBUTION_RESEARCH.md +135 -0
  75. package/docs/START_WORKFLOW.md +19 -7
  76. package/docs/SUPPORTED_AI_APPS.md +85 -33
  77. package/docs/TODO.md +1 -1
  78. package/docs/WORKFLOWS.md +8 -6
  79. package/docs/superpowers/specs/2026-06-16-demo-dashboard-deploy-design.md +64 -0
  80. package/package.json +8 -3
  81. package/packages/core/src/services.ts +42 -1
  82. package/packages/db/src/connection.ts +30 -0
  83. package/packages/db/src/index.ts +1 -0
  84. package/packages/db/src/paths.ts +11 -0
  85. package/packages/db/src/repositories.ts +109 -63
  86. package/packages/db/src/schema.ts +118 -0
  87. package/packages/exporters/src/html.ts +9 -12
  88. package/packages/mcp-tools/src/bridge.ts +55 -32
  89. package/packages/mcp-tools/src/index.ts +9 -0
  90. package/packages/mcp-tools/src/tools.ts +269 -65
  91. package/packages/mcp-tools/src/update.ts +330 -0
  92. package/packages/parsers/src/documents.ts +521 -0
  93. package/packages/parsers/src/index.ts +8 -5
  94. package/packages/prompts/src/onboarding.ts +2 -2
  95. package/packages/shared/src/build-info.ts +22 -0
  96. package/packages/shared/src/enums.ts +39 -0
  97. package/packages/shared/src/index.ts +1 -0
  98. package/packages/shared/src/schemas.ts +24 -3
  99. package/packages/workflows/src/definitions.ts +4 -2
  100. package/scripts/build-demo.mjs +130 -0
  101. package/scripts/build-dist.mjs +46 -8
  102. package/scripts/build-mcpb.mjs +60 -4
  103. package/scripts/doctor.ts +16 -3
  104. package/scripts/gen-og.mjs +86 -0
  105. package/scripts/init.ts +32 -3
  106. package/scripts/seed.ts +5 -5
  107. package/scripts/shots-seed.ts +192 -0
  108. package/scripts/shots.ts +89 -0
  109. package/tsconfig.json +3 -1
  110. package/dist/careermate.mcpb +0 -0
  111. package/dist/mcp-smoke.err +0 -3
  112. package/dist/mcpb-stage/README.md +0 -219
  113. package/dist/mcpb-stage/dist/install-page/index.html +0 -434
  114. package/dist/mcpb-stage/dist/install-page/style.css +0 -407
  115. package/dist/mcpb-stage/dist/install-page/vercel.json +0 -20
  116. package/dist/mcpb-stage/dist/mcp.mjs +0 -23704
  117. package/dist/mcpb-stage/dist/public/app.js +0 -118
  118. package/dist/mcpb-stage/dist/public/index.html +0 -41
  119. package/dist/mcpb-stage/dist/public/lib.js +0 -282
  120. package/dist/mcpb-stage/dist/public/pages/applications.js +0 -98
  121. package/dist/mcpb-stage/dist/public/pages/documents.js +0 -446
  122. package/dist/mcpb-stage/dist/public/pages/home.js +0 -263
  123. package/dist/mcpb-stage/dist/public/pages/interview.js +0 -230
  124. package/dist/mcpb-stage/dist/public/pages/jobs.js +0 -494
  125. package/dist/mcpb-stage/dist/public/pages/profile.js +0 -576
  126. package/dist/mcpb-stage/dist/public/pages/settings.js +0 -233
  127. package/dist/mcpb-stage/dist/public/styles.css +0 -420
  128. package/dist/mcpb-stage/manifest.json +0 -40
  129. package/docs/INSTALL.md +0 -148
  130. package/docs/INSTALL_AND_USAGE.md +0 -99
  131. package/install-page/index.html +0 -474
  132. package/install-page/style.css +0 -391
  133. package/install-page/vercel.json +0 -20
  134. /package/dist/{mcpb-stage/dist/public → install-page}/fonts/PretendardVariable.woff2 +0 -0
@@ -0,0 +1,192 @@
1
+ /**
2
+ * `shots-seed` — richer demo data for marketing screenshots ONLY.
3
+ *
4
+ * Unlike `scripts/seed.ts` (one job, for a first-run feel), this fills the whole
5
+ * pipeline: jobs across every status, a few cover letters, fit analyses, and an
6
+ * interview prep — so the home dashboard, the applications board and the jobs
7
+ * list all look populated in the landing-page screenshots.
8
+ *
9
+ * NEVER run this against real data. Always isolate into a throwaway dir:
10
+ * CAREERMATE_DATA_DIR=./.shots-data npm run shots:seed
11
+ */
12
+ import {
13
+ profileRepo, experienceRepo, projectRepo, skillRepo, getDataDir,
14
+ } from '@careermate/db';
15
+ import {
16
+ saveProfile, saveJobPosting, saveFitAnalysis, saveCoverLetterVersion,
17
+ updateApplicationStatus, saveInterviewPrep, addResume,
18
+ } from '@careermate/core';
19
+
20
+ if (profileRepo.get()?.name) {
21
+ console.error('⚠️ 데이터가 이미 있습니다. 마케팅 시드는 빈 throwaway 폴더에서만 실행하세요.');
22
+ console.error(' 예: CAREERMATE_DATA_DIR=./.shots-data npm run shots:seed');
23
+ process.exit(1);
24
+ }
25
+
26
+ saveProfile({
27
+ name: '홍길동',
28
+ email: 'gildong.hong@example.com',
29
+ phone: '010-1234-5678',
30
+ location: '서울',
31
+ headline: '5년 차 백엔드 엔지니어 · 결제/정산 도메인',
32
+ summary: '대규모 트래픽 환경에서 결제·정산 시스템을 설계·운영했습니다. 안정성과 데이터 정합성을 중시하며, 문제를 정량적으로 정의하고 개선하는 것을 좋아합니다.',
33
+ desired_roles: ['백엔드 엔지니어', '서버 개발자', '플랫폼 엔지니어'],
34
+ desired_conditions: '서울/판교, 정규직, 원격 일부 가능, 결제/금융 도메인 선호',
35
+ preferred_tone: '담백하고 구체적이며, 성과를 숫자로 보여주는 문체',
36
+ emphasis_points: ['대규모 트래픽 처리 경험', '데이터 정합성', '장애 대응', '동료와의 협업'],
37
+ links: [
38
+ { label: 'GitHub', url: 'https://github.com/example' },
39
+ { label: '기술 블로그', url: 'https://blog.example.com' },
40
+ ],
41
+ });
42
+
43
+ experienceRepo.add({
44
+ company: '페이테크', role: '백엔드 엔지니어', employment_type: '정규직',
45
+ start_date: '2021-03', is_current: true,
46
+ description: '결제 게이트웨이와 정산 시스템 개발·운영',
47
+ achievements: ['결제 승인 API p99 응답시간 320ms→110ms 개선', '정산 배치 처리 시간 6시간→40분 단축', '장애 0건으로 연간 거래액 1.2조 처리'],
48
+ tech: ['Java', 'Spring Boot', 'Kafka', 'MySQL', 'Redis', 'Kubernetes'],
49
+ });
50
+ experienceRepo.add({
51
+ company: '커머스랩', role: '주니어 백엔드 개발자', employment_type: '정규직',
52
+ start_date: '2019-01', end_date: '2021-02',
53
+ description: '커머스 주문/재고 서비스 개발',
54
+ achievements: ['주문 동시성 이슈 해결로 재고 오차 99% 감소', '검색 응답속도 2배 개선'],
55
+ tech: ['Python', 'Django', 'PostgreSQL', 'Elasticsearch'],
56
+ });
57
+
58
+ projectRepo.add({
59
+ name: '실시간 정산 파이프라인', role: '설계 및 리드',
60
+ description: 'Kafka 기반 이벤트 스트리밍으로 준실시간 정산 파이프라인 구축',
61
+ highlights: ['정산 지연 6시간→실시간', '재처리 가능한 멱등 설계'],
62
+ tech: ['Kafka', 'Spring', 'MySQL'], url: 'https://github.com/example/settlement',
63
+ });
64
+ projectRepo.add({
65
+ name: '결제 장애 자동 복구 시스템', role: '개발',
66
+ description: 'PG사 장애 시 자동 우회 라우팅 및 알림',
67
+ highlights: ['장애 평균 복구시간 15분→2분'], tech: ['Java', 'Spring', 'Redis'],
68
+ });
69
+
70
+ for (const s of [
71
+ { name: 'Java', category: '언어', level: '상', years: 5 },
72
+ { name: 'Spring Boot', category: '프레임워크', level: '상', years: 5 },
73
+ { name: 'Kafka', category: '인프라', level: '중', years: 3 },
74
+ { name: 'MySQL', category: '데이터베이스', level: '상', years: 5 },
75
+ { name: 'Kubernetes', category: '인프라', level: '중', years: 2 },
76
+ { name: 'Python', category: '언어', level: '중', years: 3 },
77
+ ]) skillRepo.add(s);
78
+
79
+ addResume({
80
+ kind: 'resume', title: '홍길동 이력서 (백엔드)', is_primary: true, source: 'manual',
81
+ content: `# 홍길동 — 백엔드 엔지니어\n\n## 경력 요약\n결제·정산 도메인 5년. 대규모 트래픽 안정화와 데이터 정합성에 강점.\n\n## 핵심 성과\n- 결제 승인 API p99 320ms→110ms\n- 정산 배치 6시간→40분\n- 연간 거래액 1.2조 무중단 처리`,
82
+ });
83
+
84
+ /** One demo job: posting → optional fit analysis → optional cover letter → status. */
85
+ function job(spec: {
86
+ company: string; position: string; location: string; deadline: string;
87
+ description: string; requirements: string[]; keywords: string[];
88
+ status: string; score?: number; fit?: { summary: string; strengths: string[]; gaps: string[]; matched: string[]; missing: string[] };
89
+ cover?: { title: string; content: string };
90
+ }) {
91
+ const { job: j } = saveJobPosting({
92
+ company: spec.company, position: spec.position, url: `https://example.com/${encodeURIComponent(spec.company)}`,
93
+ location: spec.location, employment_type: '정규직', description: spec.description,
94
+ requirements: spec.requirements, keywords: spec.keywords, deadline: spec.deadline, source: '데모',
95
+ });
96
+ if (spec.fit && spec.score != null) {
97
+ saveFitAnalysis({
98
+ job_id: j.id, score: spec.score, summary: spec.fit.summary,
99
+ strengths: spec.fit.strengths, gaps: spec.fit.gaps,
100
+ matched_keywords: spec.fit.matched, missing_keywords: spec.fit.missing,
101
+ recommendations: ['지원동기에 결제 도메인 경험을 구체 수치로', '부족 키워드는 학습 의지로 보완'],
102
+ });
103
+ }
104
+ let coverLetterId: number | undefined;
105
+ if (spec.cover) {
106
+ const { coverLetter } = saveCoverLetterVersion({
107
+ job_id: j.id, title: spec.cover.title, content: spec.cover.content, note: '초안', source: 'ai',
108
+ });
109
+ coverLetterId = coverLetter.id;
110
+ }
111
+ if (spec.status !== 'draft') updateApplicationStatus(j.id, spec.status, `데모: ${spec.status}`);
112
+ return { jobId: j.id, coverLetterId };
113
+ }
114
+
115
+ // 최종 합격
116
+ job({
117
+ company: '쿠팡', position: '백엔드 엔지니어 (정산플랫폼)', location: '서울 송파', deadline: '2026-05-30',
118
+ description: '대규모 정산 플랫폼의 안정성과 확장성을 책임질 백엔드 엔지니어를 찾습니다.',
119
+ requirements: ['Java/Kotlin 백엔드', '대규모 트래픽', '정산 도메인'], keywords: ['Java', 'Spring', 'Kafka', '정산', 'MSA'],
120
+ status: 'final_passed', score: 91,
121
+ fit: { summary: '정산 도메인 경험이 공고와 거의 완벽히 일치합니다.', strengths: ['정산 파이프라인 설계 경험', '대규모 트래픽 안정화'], gaps: ['Kotlin 실무 경험'], matched: ['Java', 'Spring', 'Kafka', '정산'], missing: ['Kotlin'] },
122
+ cover: { title: '쿠팡 정산플랫폼 자기소개서', content: '저는 "정산은 단 1원도 틀려선 안 된다"는 원칙으로 일해 왔습니다...(데모)' },
123
+ });
124
+
125
+ // 면접
126
+ const naver = job({
127
+ company: '네이버', position: '서버 개발자 (페이)', location: '경기 성남', deadline: '2026-07-10',
128
+ description: '네이버페이 결제/정산 백엔드를 함께 만들 서버 개발자를 찾습니다.',
129
+ requirements: ['Java 백엔드', '결제 시스템 경험', '대용량 처리'], keywords: ['Java', 'Spring', '결제', '대용량'],
130
+ status: 'interview', score: 84,
131
+ fit: { summary: '결제 백엔드 경험과 강하게 일치하며 대용량 처리 성과가 돋보입니다.', strengths: ['결제 API 성능 개선(p99)', '대용량 트래픽 경험'], gaps: ['검색 도메인 경험은 제한적'], matched: ['Java', 'Spring', '결제'], missing: ['대용량 검색'] },
132
+ cover: { title: '네이버페이 자기소개서', content: '결제 시스템의 신뢰성을 숫자로 증명해 온 백엔드 엔지니어입니다...(데모)' },
133
+ });
134
+ saveInterviewPrep({
135
+ job_id: naver.jobId,
136
+ questions: [
137
+ { question: '결제 승인 API 응답시간을 어떻게 개선했나요?', intent: '성능 개선 접근법', followups: ['병목은 어떻게 찾았나요?'], answer_outline: '프로파일링으로 커넥션 풀 병목 발견 → 캐시 도입 → p99 320→110ms.' },
138
+ { question: '데이터 정합성이 깨질 뻔한 경험은?', intent: '장애 대응', followups: ['멱등성 보장은?'], answer_outline: '정산 재처리 중복 지급 위험 → 멱등키+분산락으로 해결.' },
139
+ ],
140
+ star_guides: [
141
+ { question: '가장 큰 기술적 성과', situation: '정산 배치 6시간 소요', task: '처리 시간 단축', action: 'Kafka 이벤트 스트리밍 재설계', result: '6시간→40분, 지연 0건' },
142
+ ],
143
+ self_introduction: '안녕하세요. 결제·정산 도메인에서 5년간 "정확함"을 지켜온 백엔드 엔지니어 홍길동입니다...(데모 1분 자기소개)',
144
+ notes: '결제 도메인 질문 집중 예상. 정량 수치 암기.',
145
+ });
146
+
147
+ // 서류 합격
148
+ const toss = job({
149
+ company: '토스', position: '백엔드 엔지니어 (결제)', location: '서울 강남', deadline: '2026-07-15',
150
+ description: '토스 결제 시스템의 안정성과 확장성을 책임질 백엔드 엔지니어를 찾습니다.',
151
+ requirements: ['Java/Kotlin 백엔드', '대규모 트래픽', '결제/정산 도메인 이해'], keywords: ['Java', 'Kotlin', 'Spring', 'Kafka', '결제', 'MSA'],
152
+ status: 'document_passed', score: 88,
153
+ fit: { summary: '결제·정산 경험과 대규모 트래픽 이력이 요구사항과 강하게 일치합니다.', strengths: ['결제/정산 5년', 'p99 개선 성과', 'Kafka 이벤트 처리'], gaps: ['Kotlin 실무 경험 부족', 'MSA 리딩 제한적'], matched: ['Java', 'Spring', 'Kafka', '결제'], missing: ['Kotlin', 'MSA'] },
154
+ cover: { title: '토스 백엔드 자기소개서', content: '저는 결제 시스템의 "한 푼도 틀리면 안 된다"는 긴장감 속에서 성장했습니다...(데모)' },
155
+ });
156
+ saveCoverLetterVersion({
157
+ cover_letter_id: toss.coverLetterId,
158
+ content: '저는 결제 시스템의 "한 푼도 틀리면 안 된다"는 긴장감 속에서 성장했습니다. 페이테크에서 결제 승인 API의 p99 응답시간을 320ms에서 110ms로 줄이며...(데모 v2)',
159
+ note: '지원동기와 정량 성과 보강', source: 'edit',
160
+ });
161
+
162
+ // 지원 완료
163
+ job({
164
+ company: '카카오페이', position: '백엔드 엔지니어 (정산)', location: '경기 판교', deadline: '2026-07-20',
165
+ description: '카카오페이 정산 시스템 백엔드 개발자를 찾습니다.',
166
+ requirements: ['Java/Kotlin', 'MSA', '정산/회계 이해'], keywords: ['Java', 'Kotlin', 'MSA', '정산'],
167
+ status: 'applied', score: 79,
168
+ fit: { summary: '정산 경험은 강하나 MSA 전환 리딩 경험 보강이 필요합니다.', strengths: ['정산 도메인 경험', '데이터 정합성'], gaps: ['MSA 리딩', 'Kotlin'], matched: ['Java', '정산'], missing: ['Kotlin', 'MSA'] },
169
+ cover: { title: '카카오페이 자기소개서', content: '정산의 정확성과 속도를 동시에 끌어올린 경험을 가지고 있습니다...(데모)' },
170
+ });
171
+
172
+ // 지원 예정
173
+ job({
174
+ company: '당근', position: '서버 개발자 (커머스)', location: '서울 강남', deadline: '2026-08-01',
175
+ description: '당근 중고거래/커머스 백엔드 서버 개발자를 찾습니다.',
176
+ requirements: ['Java/Kotlin 또는 Go', '대용량 트래픽', '커머스 도메인'], keywords: ['Java', 'Go', '대용량', '커머스'],
177
+ status: 'planned', score: 73,
178
+ fit: { summary: '대용량 처리 경험은 적합하나 커머스 도메인 노출은 초기 경력에 한정됩니다.', strengths: ['대용량 트래픽', '주문/재고 경험'], gaps: ['최근 커머스 경험', 'Go'], matched: ['Java', '대용량'], missing: ['Go', '커머스'] },
179
+ });
180
+
181
+ // 보류
182
+ job({
183
+ company: '라인', position: '백엔드 엔지니어 (Fintech)', location: '서울 영등포', deadline: '2026-08-10',
184
+ description: 'LINE Fintech 백엔드 엔지니어를 찾습니다.',
185
+ requirements: ['Java/Kotlin', '글로벌 서비스 경험', '금융 도메인'], keywords: ['Java', 'Kotlin', '금융', '글로벌'],
186
+ status: 'on_hold', score: 70,
187
+ fit: { summary: '금융 도메인은 적합하나 글로벌 서비스 경험이 부족합니다.', strengths: ['금융/결제 도메인'], gaps: ['글로벌 서비스', 'Kotlin'], matched: ['Java', '금융'], missing: ['글로벌', 'Kotlin'] },
188
+ });
189
+
190
+ console.log('✅ 마케팅용 데모 데이터를 생성했습니다.');
191
+ console.log(` 위치: ${getDataDir()}`);
192
+ console.log(' 공고 6건(최종합격·면접·서류합격·지원·예정·보류) · 적합도 6 · 자기소개서 4 · 면접 준비 1');
@@ -0,0 +1,89 @@
1
+ /**
2
+ * `shots` — capture real dashboard screenshots for the landing page.
3
+ *
4
+ * Starts the local web server against whatever data dir is configured, then uses
5
+ * Playwright to capture home / applications / jobs in BOTH light and dark themes
6
+ * into install-page/shots/{screen}-{theme}.png. The landing page embeds these and
7
+ * swaps light↔dark to match its own theme toggle.
8
+ *
9
+ * Run against the throwaway marketing data (see shots-seed.ts):
10
+ * CAREERMATE_DATA_DIR=./.shots-data CAREERMATE_NO_OPEN=1 npm run shots
11
+ */
12
+ import fs from 'node:fs';
13
+ import path from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+ import { chromium } from 'playwright';
16
+ import { startServer } from '../apps/web/src/server.ts';
17
+ import { getDataDir } from '@careermate/db';
18
+
19
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
20
+ const OUT_DIR = path.resolve(__dirname, '../install-page/shots');
21
+ fs.mkdirSync(OUT_DIR, { recursive: true });
22
+
23
+ const SCREENS = [
24
+ { name: 'home', hash: '#/home' },
25
+ { name: 'applications', hash: '#/applications' },
26
+ { name: 'jobs', hash: '#/jobs' },
27
+ { name: 'documents', hash: '#/documents' },
28
+ { name: 'interview', hash: '#/interview' },
29
+ { name: 'settings', hash: '#/settings' },
30
+ ];
31
+ const THEMES = ['light', 'dark'] as const;
32
+
33
+ // A desktop viewport that fits the home/board content without scrolling; @2x for
34
+ // crisp text on retina/landing-card sizes.
35
+ const VIEWPORT = { width: 1360, height: 940 };
36
+
37
+ async function main(): Promise<void> {
38
+ process.env.CAREERMATE_NO_OPEN = '1';
39
+ const { url } = await startServer(Number(process.env.CAREERMATE_PORT ?? 4337));
40
+ const expectedDir = getDataDir();
41
+ console.log(`서버 시작: ${url} (data: ${expectedDir})`);
42
+
43
+ // Safety: never screenshot real career data. Confirm the server we'll capture is
44
+ // actually serving our throwaway demo dir — not another careermate instance that
45
+ // grabbed the URL (e.g. a real dashboard on the default port).
46
+ const health = await fetch(`${url}/api/health`).then((r) => r.json()).catch(() => null) as { data_dir?: string } | null;
47
+ if (!health || path.resolve(health.data_dir ?? '') !== path.resolve(expectedDir)) {
48
+ throw new Error(`연결된 서버의 데이터 폴더(${health?.data_dir ?? '?'})가 예상(${expectedDir})과 다릅니다. `
49
+ + '실제 데이터를 캡처할 위험이 있어 중단합니다. CAREERMATE_DATA_DIR을 빈 throwaway 폴더로 지정하세요.');
50
+ }
51
+
52
+ const browser = await chromium.launch();
53
+ try {
54
+ for (const theme of THEMES) {
55
+ const ctx = await browser.newContext({ viewport: VIEWPORT, deviceScaleFactor: 2 });
56
+ // The dashboard reads localStorage 'cf-theme' on boot (app.js initTheme).
57
+ await ctx.addInitScript((t) => {
58
+ try { window.localStorage.setItem('cf-theme', t as string); } catch { /* ignore */ }
59
+ }, theme);
60
+ const page = await ctx.newPage();
61
+
62
+ for (const s of SCREENS) {
63
+ await page.goto(`${url}/${s.hash}`, { waitUntil: 'networkidle' });
64
+ // wait for the route to render (spinner gone) and webfont to load
65
+ await page.waitForFunction(() => {
66
+ const v = document.getElementById('view');
67
+ const hasSpinner = !!document.querySelector('.spinner');
68
+ const fontsReady = !document.fonts || document.fonts.status === 'loaded';
69
+ return !!v && v.childElementCount > 0 && !hasSpinner && fontsReady;
70
+ }, { timeout: 10_000 }).catch(() => { /* best effort */ });
71
+ await page.waitForTimeout(450);
72
+
73
+ const file = path.join(OUT_DIR, `${s.name}-${theme}.png`);
74
+ await page.screenshot({ path: file });
75
+ console.log(` ✓ ${path.relative(process.cwd(), file)}`);
76
+ }
77
+ await ctx.close();
78
+ }
79
+ } finally {
80
+ await browser.close();
81
+ }
82
+ console.log('완료. install-page/shots/*.png 생성됨.');
83
+ process.exit(0);
84
+ }
85
+
86
+ main().catch((err) => {
87
+ console.error('스크린샷 캡처 실패:', err instanceof Error ? err.stack : err);
88
+ process.exit(1);
89
+ });
package/tsconfig.json CHANGED
@@ -25,5 +25,7 @@
25
25
  "@careermate/workflows": ["packages/workflows/src/index.ts"]
26
26
  }
27
27
  },
28
- "include": ["packages/**/*.ts", "apps/**/*.ts", "scripts/**/*.ts"]
28
+ "include": ["packages/**/*.ts", "apps/**/*.ts", "scripts/**/*.ts"],
29
+ "//exclude": "scripts/shots*.ts는 Playwright 브라우저 컨텍스트(window/document)·임시 시드용 마케팅 도구라 타입검사 제외(배포물 아님).",
30
+ "exclude": ["node_modules", "scripts/shots.ts", "scripts/shots-seed.ts"]
29
31
  }
Binary file
@@ -1,3 +0,0 @@
1
- [careermate-mcp] 연결됨 · 도구 24개 · 데이터: C:\Users\승택\.careermate
2
- (node:23944) ExperimentalWarning: SQLite is an experimental feature and might change at any time
3
- (Use `node --trace-warnings ...` to show where the warning was created)
@@ -1,219 +0,0 @@
1
- # CareerMate
2
-
3
- **MCP 우선(MCP-first) 로컬 커리어 관리 도구.** ChatGPT·Claude·Gemini 같은 당신의 AI를 인터페이스로 쓰고, CareerMate는 당신의 커리어 데이터를 **이 컴퓨터에만** 저장한 뒤 MCP로 노출합니다.
4
-
5
- ---
6
-
7
- ## 핵심 철학
8
-
9
- - **MCP 우선** — 새 앱 사용법을 익힐 필요가 없습니다. 평소 쓰던 AI(ChatGPT·Claude·Gemini)와 대화하면, 그 AI가 CareerMate의 로컬 MCP 도구를 호출해 당신의 커리어 DB를 읽고 씁니다.
10
- - **로컬 우선** — 모든 데이터는 당신의 컴퓨터에만 저장됩니다(`~/.careermate`). 외부 서버로 전송하지 않습니다. 대시보드 웹 서버는 `127.0.0.1`(이 컴퓨터)에만 연결됩니다.
11
- - **LLM 비내장** — CareerMate 안에는 AI가 들어 있지 않습니다. 분석·글쓰기 같은 “생각하는 일”은 **당신의 AI**가 하고, CareerMate는 데이터를 **안전하게 보관·조회**하는 역할만 합니다.
12
-
13
- > 쉽게 말해: 당신의 AI가 “두뇌”, CareerMate는 그 두뇌가 꺼내 쓰는 “커리어 서랍장”입니다.
14
-
15
- ---
16
-
17
- ## 무엇을 하나요?
18
-
19
- 프로필·이력서·자기소개서·채용공고·지원 현황·면접 준비를 로컬 SQLite에 구조화해 저장하고, AI 어시스턴트가 MCP 도구로 그 데이터를 읽고 씁니다. 전형적인 흐름은 이렇습니다.
20
-
21
- 1. 프로필·이력서 등록 → 2. 채용공고 저장·파싱 → 3. 적합도(핏) 분석 → 4. 맞춤 자기소개서 작성·버전 관리 → 5. 지원 상태 관리 → 6. 면접 준비
22
-
23
- 언제든 대시보드를 열어 저장된 데이터를 눈으로 확인할 수 있습니다. 자세한 단계별 런북은 [`docs/START_WORKFLOW.md`](docs/START_WORKFLOW.md)를 참고하세요.
24
-
25
- ---
26
-
27
- ## 주요 기능
28
-
29
- - **AI와 대화로 모든 작업** — MCP 도구 23개로 온보딩·프로필·이력서·자소서·공고·핏 분석·지원 상태·면접 준비까지 처리.
30
- - **지원 상태 8단계 관리** — `draft`(작성 중) · `planned`(지원 예정) · `applied`(지원 완료) · `document_passed`(서류 합격) · `interview`(면접 진행) · `final_passed`(최종 합격) · `rejected`(불합격) · `on_hold`(보류). `document_passed` 이상에서 면접 준비가 해금됩니다.
31
- - **자기소개서 버전 관리** — 공고별로 자소서 버전을 쌓고 타임라인으로 비교, 파일로 내보내기.
32
- - **채용공고 파싱** — 붙여넣은 공고 텍스트를 구조화해 저장.
33
- - **로컬 대시보드** — 프레임워크·CDN 없는 바닐라 JS로 만든 7페이지 웹 화면(다크모드 지원).
34
- - **데이터 내보내기/삭제** — Settings 페이지에서 직접.
35
-
36
- ---
37
-
38
- ## 구성
39
-
40
- 두 개의 로컬 프로세스가 같은 데이터베이스를 공유합니다.
41
-
42
- | 프로세스 | 실행 | 역할 |
43
- | --- | --- | --- |
44
- | **대시보드 웹 서버** | `npm start` | `http://127.0.0.1:4319` — 내 데이터를 눈으로 확인·관리. `127.0.0.1`에만 바인딩. |
45
- | **MCP 서버** | `npm run mcp` | stdio 기반. 보통 AI 클라이언트가 자동 실행. 도구 23개 제공. |
46
-
47
- ---
48
-
49
- ## 빠른 시작
50
-
51
- ### 요구사항
52
-
53
- - **Node.js >= 22.5.0** — 내장 `node:sqlite`를 사용하므로 컴파일러나 네이티브 빌드가 **필요 없습니다**.
54
- - 버전 확인:
55
- ```bash
56
- node --version
57
- ```
58
- `v22.5.0` 미만이거나 명령을 찾지 못하면 https://nodejs.org 에서 최신 LTS를 설치하세요.
59
-
60
- ### 1) 설치
61
-
62
- 빌드 단계가 없습니다. TypeScript는 `tsx`로 바로 실행됩니다.
63
-
64
- ```bash
65
- npm install
66
- ```
67
-
68
- ### 2) 대시보드 실행
69
-
70
- ```bash
71
- npm start
72
- ```
73
-
74
- - 기본 주소는 `http://127.0.0.1:4319` 이며 브라우저가 자동으로 열립니다.
75
- - 포트가 사용 중이면 자동으로 다음 빈 포트로 폴백하니, 터미널에 출력된 실제 주소를 확인하세요.
76
- - 종료는 터미널에서 `Ctrl+C`.
77
-
78
- ### 3) 데모 데이터로 둘러보기(선택)
79
-
80
- 빈 화면이 낯설다면 예시 데이터를 넣어 기능을 미리 체험할 수 있습니다.
81
-
82
- ```bash
83
- npm run seed
84
- ```
85
-
86
- ### 4) AI(MCP)에 연결
87
-
88
- 당신의 AI가 CareerMate를 사용하려면 AI 클라이언트에 MCP 서버를 등록해야 합니다. Claude Desktop·ChatGPT·Cursor 등 클라이언트별 설정과 검증 방법은 **[`docs/INSTALL.md`](docs/INSTALL.md)** 를 따라가세요.
89
-
90
- ---
91
-
92
- ## 대시보드 7페이지
93
-
94
- | 페이지 | 설명 |
95
- | --- | --- |
96
- | **Home** | 현재 상태·다음 할 일 한눈에. |
97
- | **Profile** | 프로필, 경력(experiences), 프로젝트, 스킬 관리. |
98
- | **Jobs** | 저장한 채용공고 목록과 상세(공고 내용·핏 분석). |
99
- | **Applications** | 지원 현황을 8단계 상태로 보는 칸반 보드. |
100
- | **Documents** | 자기소개서 버전 타임라인과 이력서. |
101
- | **Interview** | 면접 준비 자료(`document_passed` 이상에서 해금). |
102
- | **Settings** | 데이터 내보내기·삭제, 환경 정보. |
103
-
104
- 다크모드를 지원합니다.
105
-
106
- ---
107
-
108
- ## MCP 도구 한눈에 (23개)
109
-
110
- | 분류 | 도구 |
111
- | --- | --- |
112
- | 온보딩 | `get_onboarding_status` · `start_onboarding` |
113
- | 프로필 | `save_profile` · `get_profile` · `update_profile` |
114
- | 이력서 | `add_resume` · `get_resumes` |
115
- | 자기소개서 | `add_cover_letter` · `get_cover_letters` · `save_cover_letter_version` · `export_cover_letter` |
116
- | 채용공고 | `save_job_posting` · `get_job_posting` · `list_jobs` · `parse_job_posting` |
117
- | 핵심 컨텍스트 | **`get_application_context`** (지원에 필요한 맥락을 한 번에 모아줌) |
118
- | 핏 분석 | `save_fit_analysis` |
119
- | 지원 상태 | `update_application_status` |
120
- | 면접 준비 | `save_interview_prep` |
121
- | 대시보드/활동 | `open_dashboard` · `open_application` · `list_recent_activity` · `get_workflow_guide` |
122
-
123
- 이 도구들 위에 5종의 워크플로우(`onboarding`, `analyze_job`, `write_cover_letter`, `manage_application_status`, `prepare_interview`)가 정의되어 있어, AI가 단계별로 자연스럽게 안내합니다.
124
-
125
- ---
126
-
127
- ## 프로젝트 구조
128
-
129
- ```
130
- CareerMate/
131
- ├─ apps/
132
- │ ├─ web/ # 대시보드 + 로컬 API 서버 (npm start)
133
- │ └─ mcp/ # MCP stdio 서버 (npm run mcp)
134
- ├─ packages/
135
- │ ├─ shared/ # 공용 타입·zod 스키마·유틸
136
- │ ├─ db/ # node:sqlite DB 접근·스키마·마이그레이션
137
- │ ├─ core/ # 도메인 유스케이스
138
- │ ├─ mcp-tools/ # MCP 도구 23개 정의
139
- │ ├─ exporters/ # 내보내기(자소서 등)
140
- │ ├─ parsers/ # 채용공고 파싱
141
- │ ├─ prompts/ # 프롬프트·안내 문구
142
- │ └─ workflows/ # 워크플로우 5종
143
- ├─ install-page/ # 설치 안내 페이지
144
- ├─ docs/ # 문서
145
- ├─ scripts/ # migrate / seed / doctor / test 등
146
- └─ package.json
147
- ```
148
-
149
- - **스택**: TypeScript(ESM), 무빌드 실행(`tsx`), 내장 `node:sqlite`(네이티브 컴파일 없음), `zod`, `@modelcontextprotocol/sdk`. 대시보드는 프레임워크·CDN 없는 바닐라 JS + 자체 CSS 디자인 시스템.
150
- - **데이터 저장소**: 12개 테이블(profile, experiences, projects, skills, documents, cover_letters, cover_letter_versions, jobs, fit_analyses, applications, interview_preps, activities). 두 프로세스(대시보드·MCP)가 **같은 SQLite DB**를 공유합니다.
151
-
152
- ---
153
-
154
- ## npm 스크립트
155
-
156
- | 스크립트 | 설명 |
157
- | --- | --- |
158
- | `npm install` | 의존성 설치 (빌드 없음) |
159
- | `npm start` | 대시보드 웹 서버 실행 |
160
- | `npm run dev` | 대시보드 실행 (파일 변경 시 자동 재시작) |
161
- | `npm run mcp` | MCP 서버 실행 (stdio) |
162
- | `npm run migrate` | DB 생성/업그레이드 |
163
- | `npm run doctor` | 설치/환경 점검 |
164
- | `npm run seed` | 예시 데이터 삽입 |
165
- | `npm test` | E2E 테스트 실행 |
166
- | `npm run test:ui` | Playwright UI 스모크 테스트 |
167
- | `npm run typecheck` | 타입 검사 (`tsc --noEmit`) |
168
-
169
- ---
170
-
171
- ## 데이터 위치 & 환경변수
172
-
173
- - 기본 폴더: `~/.careermate` (Windows: `%USERPROFILE%\.careermate`).
174
- - `careermate.sqlite` — 데이터베이스 파일
175
- - `exports/` — 내보낸 파일
176
- - `backups/` — 백업
177
- - `uploads/` — 업로드 파일
178
- - `server.json` — 실행 중 핸드셰이크 정보
179
- - 환경변수로 동작을 바꿀 수 있습니다.
180
- - `CAREERMATE_DATA_DIR` — 데이터 폴더 위치 변경
181
- - `CAREERMATE_PORT` — 대시보드 포트 고정
182
- - `CAREERMATE_NO_OPEN` — 시작 시 브라우저 자동 열기 끄기
183
-
184
- ---
185
-
186
- ## 보안 / 프라이버시
187
-
188
- - **로컬 전용 바인딩** — 대시보드 서버는 `127.0.0.1`(loopback)에만 바인딩되어 외부에서 접근할 수 없습니다.
189
- - **DNS 리바인딩 차단** — Host 허용목록으로 검증.
190
- - **변경 요청 보호** — CSRF 세션 토큰(HTML `meta`로 주입), 외부 Origin 차단.
191
- - **정적 파일 보호** — 경로 traversal(상위 폴더 탈출) 차단.
192
- - **본문 크기 제한** — 요청 본문 8MB 제한.
193
- - **민감 정보 비노출** — 이력서·자기소개서 본문은 로그나 에러 응답에 노출되지 않습니다.
194
- - **외부 전송 없음** — MCP 서버는 네트워크 호출을 하지 않으며, 모든 데이터는 당신의 컴퓨터에만 남습니다. 내보내기·삭제는 대시보드 **Settings**에서 직접 할 수 있습니다.
195
-
196
- ---
197
-
198
- ## 문서
199
-
200
- - [`docs/INSTALL.md`](docs/INSTALL.md) — 설치 및 AI 클라이언트(MCP) 연결 가이드.
201
- - [`docs/START_WORKFLOW.md`](docs/START_WORKFLOW.md) — 등록부터 면접 준비까지 단계별 작업 런북.
202
-
203
- ---
204
-
205
- ## 테스트
206
-
207
- ```bash
208
- npm test # E2E 테스트 (보안·업무흐름·MCP stdio·대시보드↔MCP 동일 DB 양방향)
209
- npm run test:ui # 대시보드 페이지 렌더 스모크 (Playwright)
210
- npm run typecheck
211
- ```
212
-
213
- 기타 유용한 스크립트: `npm run migrate`(DB 생성/업그레이드), `npm run seed`(예시 데이터), `npm run doctor`(설치·환경 점검).
214
-
215
- ---
216
-
217
- ## 라이선스
218
-
219
- MIT.