careermate 0.0.1
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/AGENTS.md +33 -0
- package/INSTALL.md +230 -0
- package/LICENSE +21 -0
- package/README.md +532 -0
- package/THIRD_PARTY_NOTICES.md +57 -0
- package/bin/careermate.mjs +77 -0
- package/dist/career-os/README.md +78 -0
- package/dist/career-os/eop/cover-letter.md +46 -0
- package/dist/career-os/eop/fit-analysis.md +48 -0
- package/dist/career-os/eop/interview-prep.md +39 -0
- package/dist/career-os/eop/job-analysis.md +42 -0
- package/dist/career-os/eop/profile-extraction.md +37 -0
- package/dist/career-os/knowledge/ats.md +131 -0
- package/dist/career-os/knowledge/company-research.md +137 -0
- package/dist/career-os/knowledge/cover-letter.md +135 -0
- package/dist/career-os/knowledge/fit-matching.md +121 -0
- package/dist/career-os/knowledge/human-writing.md +118 -0
- package/dist/career-os/knowledge/interview-behavioral.md +132 -0
- package/dist/career-os/knowledge/interview-technical.md +126 -0
- package/dist/career-os/knowledge/linkedin-profile.md +152 -0
- package/dist/career-os/knowledge/networking-referrals.md +166 -0
- package/dist/career-os/knowledge/offer-evaluation-decision.md +121 -0
- package/dist/career-os/knowledge/onboarding-first-90-days.md +126 -0
- package/dist/career-os/knowledge/portfolio.md +121 -0
- package/dist/career-os/knowledge/recruiter-screen.md +105 -0
- package/dist/career-os/knowledge/rejection-triage-iteration.md +115 -0
- package/dist/career-os/knowledge/resume.md +123 -0
- package/dist/career-os/knowledge/salary-negotiation.md +123 -0
- package/dist/career-os/knowledge/verifiers/ats-compat.md +64 -0
- package/dist/career-os/knowledge/verifiers/consistency.md +61 -0
- package/dist/career-os/knowledge/verifiers/human-voice.md +82 -0
- package/dist/career-os/knowledge/verifiers/recency-staleness.md +72 -0
- package/dist/career-os/knowledge/verifiers/responsiveness-on-target.md +82 -0
- package/dist/career-os/knowledge/verifiers/truthfulness.md +67 -0
- package/dist/doctor.mjs +79094 -0
- package/dist/mcp.mjs +96108 -0
- package/dist/migrate.mjs +4735 -0
- package/dist/pdf.worker.min.mjs +28 -0
- package/dist/public/app.js +150 -0
- package/dist/public/fonts/PretendardVariable.woff2 +0 -0
- package/dist/public/icon-180.png +0 -0
- package/dist/public/index.html +52 -0
- package/dist/public/lib.js +394 -0
- package/dist/public/pages/applications.js +107 -0
- package/dist/public/pages/documents.js +467 -0
- package/dist/public/pages/home.js +261 -0
- package/dist/public/pages/interview.js +227 -0
- package/dist/public/pages/jobs.js +493 -0
- package/dist/public/pages/profile.js +556 -0
- package/dist/public/pages/settings.js +240 -0
- package/dist/public/styles.css +498 -0
- package/dist/site/analytics.js +97 -0
- package/dist/site/favicon.svg +1 -0
- package/dist/site/fonts/PretendardVariable.woff2 +0 -0
- package/dist/site/icon-180.png +0 -0
- package/dist/site/icon-512.png +0 -0
- package/dist/site/index.html +598 -0
- package/dist/site/landing.js +54 -0
- package/dist/site/llms-install.txt +217 -0
- package/dist/site/robots.txt +4 -0
- package/dist/site/sitemap.xml +21 -0
- package/dist/site/start.html +326 -0
- package/dist/site/style.css +550 -0
- package/dist/site/vercel.json +20 -0
- package/dist/web.mjs +46852 -0
- package/docs/FAQ.md +101 -0
- package/docs/MCP_TOOLS.md +311 -0
- package/docs/README.md +28 -0
- package/docs/START_WORKFLOW.md +137 -0
- package/docs/SUPPORTED_AI_APPS.md +112 -0
- package/docs/WORKFLOWS.md +202 -0
- package/docs/career-os/README.md +78 -0
- package/docs/career-os/eop/cover-letter.md +46 -0
- package/docs/career-os/eop/fit-analysis.md +48 -0
- package/docs/career-os/eop/interview-prep.md +39 -0
- package/docs/career-os/eop/job-analysis.md +42 -0
- package/docs/career-os/eop/profile-extraction.md +37 -0
- package/docs/career-os/knowledge/ats.md +131 -0
- package/docs/career-os/knowledge/company-research.md +137 -0
- package/docs/career-os/knowledge/cover-letter.md +135 -0
- package/docs/career-os/knowledge/fit-matching.md +121 -0
- package/docs/career-os/knowledge/human-writing.md +118 -0
- package/docs/career-os/knowledge/interview-behavioral.md +132 -0
- package/docs/career-os/knowledge/interview-technical.md +126 -0
- package/docs/career-os/knowledge/linkedin-profile.md +152 -0
- package/docs/career-os/knowledge/networking-referrals.md +166 -0
- package/docs/career-os/knowledge/offer-evaluation-decision.md +121 -0
- package/docs/career-os/knowledge/onboarding-first-90-days.md +126 -0
- package/docs/career-os/knowledge/portfolio.md +121 -0
- package/docs/career-os/knowledge/recruiter-screen.md +105 -0
- package/docs/career-os/knowledge/rejection-triage-iteration.md +115 -0
- package/docs/career-os/knowledge/resume.md +123 -0
- package/docs/career-os/knowledge/salary-negotiation.md +123 -0
- package/docs/career-os/knowledge/verifiers/ats-compat.md +64 -0
- package/docs/career-os/knowledge/verifiers/consistency.md +61 -0
- package/docs/career-os/knowledge/verifiers/human-voice.md +82 -0
- package/docs/career-os/knowledge/verifiers/recency-staleness.md +72 -0
- package/docs/career-os/knowledge/verifiers/responsiveness-on-target.md +82 -0
- package/docs/career-os/knowledge/verifiers/truthfulness.md +67 -0
- package/package.json +89 -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 +187 -0
- package/packages/core/src/summary.ts +104 -0
- package/packages/db/src/connection.ts +76 -0
- package/packages/db/src/index.ts +23 -0
- package/packages/db/src/paths.ts +52 -0
- package/packages/db/src/repositories.ts +891 -0
- package/packages/db/src/runtime.ts +58 -0
- package/packages/db/src/schema.ts +307 -0
- package/packages/exporters/src/html.ts +110 -0
- package/packages/exporters/src/index.ts +364 -0
- package/packages/exporters/src/markdown.ts +178 -0
- package/packages/knowledge/src/index.ts +168 -0
- package/packages/mcp-tools/src/bridge.ts +106 -0
- package/packages/mcp-tools/src/index.ts +17 -0
- package/packages/mcp-tools/src/result.ts +49 -0
- package/packages/mcp-tools/src/tools.ts +747 -0
- package/packages/mcp-tools/src/update.ts +330 -0
- package/packages/parsers/src/documents.ts +533 -0
- package/packages/parsers/src/html.ts +86 -0
- package/packages/parsers/src/index.ts +231 -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 +57 -0
- package/packages/shared/src/build-info.ts +22 -0
- package/packages/shared/src/enums.ts +142 -0
- package/packages/shared/src/index.ts +19 -0
- package/packages/shared/src/schemas.ts +453 -0
- package/packages/workflows/src/definitions.ts +109 -0
- package/packages/workflows/src/index.ts +39 -0
- package/scripts/init.ts +371 -0
- package/tsconfig.json +32 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @careermate/workflows — definitions.ts
|
|
3
|
+
*
|
|
4
|
+
* Step-by-step workflows the AI should follow, encoded as DATA + helper text.
|
|
5
|
+
* Each step references concrete MCP tool names so the AI knows exactly which
|
|
6
|
+
* tool to call. Korean-first (read by the AI and surfaced to users).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export interface WorkflowDefinition {
|
|
10
|
+
/** Stable machine id. */
|
|
11
|
+
id: string;
|
|
12
|
+
/** Human-facing title (Korean). */
|
|
13
|
+
title: string;
|
|
14
|
+
/** One-line description of what this workflow accomplishes. */
|
|
15
|
+
description: string;
|
|
16
|
+
/** When the AI should start this workflow (natural-language trigger). */
|
|
17
|
+
trigger: string;
|
|
18
|
+
/** Ordered steps; each references the concrete MCP tool(s) to call. */
|
|
19
|
+
steps: string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const WORKFLOWS: WorkflowDefinition[] = [
|
|
23
|
+
{
|
|
24
|
+
id: 'onboarding',
|
|
25
|
+
title: '온보딩 (첫 셋업)',
|
|
26
|
+
description:
|
|
27
|
+
'사용자가 처음 연결했을 때 프로필·이력서·자기소개서를 등록하고 대시보드를 여는 흐름.',
|
|
28
|
+
trigger:
|
|
29
|
+
'사용자가 처음 CareerMate에 연결했거나, "시작하자 / 셋업해줘 / 프로필 등록"이라고 요청할 때.',
|
|
30
|
+
steps: [
|
|
31
|
+
'`get_onboarding_status`를 호출해 현재 등록 상태(has_profile, has_resume, has_cover_letter, has_experience, has_skills, profile_completeness, next_steps)를 확인한다.',
|
|
32
|
+
'비어 있는 항목을 사용자에게 쉬운 한국어로 안내하고, 먼저 "기존에 작성해 둔 이력서·경력기술서 파일이 있는지" 묻는다(hwp/docx/pdf 모두 가능).',
|
|
33
|
+
'파일 입력 분기: ① 사용자가 "파일이 있다"고 하면 `open_inbox`로 인입 폴더를 열어 파일을 넣게 하고, "다 넣었다"고 하면 `read_inbox`로 본문을 읽는다(pdf·이미지는 read_inbox가 경로만 주므로 클라이언트의 파일 읽기로 직접 읽는다). ② 사용자가 파일 경로를 직접 알려주면 `read_document`로 읽는다. ③ "없다/직접 입력/텍스트로 붙여넣기"면 폴더를 열지 말고 받은 텍스트를 그대로 쓴다. (묻지도 않고 open_inbox로 폴더를 자동으로 열지 않는다.)',
|
|
34
|
+
'수집한 정보를 구조화해 `save_profile`로 저장한다(이름·연락처·headline·summary·desired_roles·desired_conditions, 그리고 글쓰기 선호인 preferred_tone과 emphasis_points 포함). 파일 출처는 source=upload, 직접 입력은 manual.',
|
|
35
|
+
'업로드한 이력서/경력기술서/포트폴리오 본문을 `add_resume`로 저장한다(kind·title 지정, 대표 문서는 is_primary=true).',
|
|
36
|
+
'경력·프로젝트·스킬이 파악되면 `add_experience`·`add_project`·`add_skill`로 각각 저장한다(완성도의 has_experience/has_skills에 반영).',
|
|
37
|
+
'기존 자기소개서가 있으면 `save_cover_letter_version`으로 저장한다(title+content면 v1로 저장, 없으면 나중에 작성 가능함을 안내).',
|
|
38
|
+
'`open_dashboard`를 호출해 사용자가 자기 데이터를 확인하게 한다.',
|
|
39
|
+
'다시 `get_onboarding_status`로 완성도를 확인하고, 다음 단계(공고 분석 등)를 제안한다.',
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'analyze_job',
|
|
44
|
+
title: '공고 분석 (적합도 분석)',
|
|
45
|
+
description:
|
|
46
|
+
'채용 공고를 읽어 사용자 프로필과 비교하고, 공고와 적합도 분석을 저장한 뒤 자기소개서 작성을 제안하는 핵심 흐름.',
|
|
47
|
+
trigger:
|
|
48
|
+
'사용자가 채용 공고 링크나 본문을 붙여넣거나 "이 공고 분석해줘 / 나랑 잘 맞아?"라고 물을 때.',
|
|
49
|
+
steps: [
|
|
50
|
+
'공고 원문(붙여넣은 텍스트 또는 링크 내용)을 읽고 company, position, requirements, keywords, deadline 등 핵심 정보를 정리한다.',
|
|
51
|
+
'`get_application_context`를 호출해 한 번에 프로필·대표 이력서·경력·프로젝트·스킬·기존 자기소개서·최근 지원 현황·이전 적합도 분석·같은 회사/직무 관련 기록·글쓰기 선호(preferred_tone, emphasis_points)를 가져온다. (분석 대상 공고가 이미 저장돼 있으면 job_id도 함께 전달한다.)',
|
|
52
|
+
'공고 요구사항과 사용자 경력/스킬/프로젝트를 비교한다: 일치하는 강점(strengths)과 부족한 부분(gaps), 매칭 키워드(matched_keywords)와 누락 키워드(missing_keywords)를 도출하고, 종합 적합도 점수(score 0~100)와 요약(summary), 지원 전략 제안(recommendations)을 정리한다. 없는 경험을 지어내지 않는다.',
|
|
53
|
+
'아직 저장되지 않은 공고라면 `save_job_posting`으로 공고를 저장하고 job_id를 확보한다.',
|
|
54
|
+
'도출한 분석 결과를 `save_fit_analysis`로 저장한다(job_id, score, summary, strengths, gaps, matched_keywords, missing_keywords, recommendations).',
|
|
55
|
+
'분석 결과를 사용자에게 쉬운 한국어로 설명한다(잘 맞는 점, 보완할 점, 추천 전략). 기술 용어는 빼고 전달한다.',
|
|
56
|
+
'"이 공고에 맞춘 자기소개서를 써드릴까요?"라고 자기소개서 작성을 제안한다.',
|
|
57
|
+
'사용자가 동의하면 자기소개서를 작성하기 직전에 `get_writing_style_guide`를 호출해 "AI 티 안 나는 글쓰기 규칙"을 가져온다.',
|
|
58
|
+
'적합도 분석·글쓰기 선호와 글쓰기 규칙을 함께 반영해 자기소개서를 작성하고(번역투·클리셰·기계적 병렬 제거, 사실·수치는 그대로), `save_cover_letter_version`으로 저장한다(job_id 연결, note에 작성 의도 요약).',
|
|
59
|
+
'`open_application`으로 해당 지원 건을 열어 사용자가 결과를 확인하게 하고, 지원 상태 변경(예: 지원 예정/지원 완료)을 제안한다.',
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'write_cover_letter',
|
|
64
|
+
title: '자기소개서 작성',
|
|
65
|
+
description:
|
|
66
|
+
'특정 공고에 맞춘 자기소개서를 사용자 확인을 거쳐 작성하고 버전으로 저장하는 흐름.',
|
|
67
|
+
trigger:
|
|
68
|
+
'사용자가 "자기소개서 써줘 / 이 공고용 자소서 만들어줘"라고 요청하거나, 공고 분석 후 작성 제안에 동의했을 때.',
|
|
69
|
+
steps: [
|
|
70
|
+
'`get_application_context`(가능하면 job_id 포함)를 호출해 프로필·경력·프로젝트·스킬·기존 자기소개서·이전 적합도 분석·글쓰기 선호(preferred_tone, emphasis_points)를 가져온다.',
|
|
71
|
+
'작성 전에 대상 공고, 강조할 포인트, 톤, 분량을 사용자에게 확인한다(확인 없이 큰 작업을 진행하지 않는다).',
|
|
72
|
+
'`get_writing_style_guide`를 호출해 "AI 티 안 나는 글쓰기 규칙"(번역투·클리셰·기계적 병렬·상투적 연결어·균일한 문장 리듬 제거)을 가져온다.',
|
|
73
|
+
'저장된 실제 경험·성과만으로 자기소개서를 작성하되, 위 글쓰기 규칙을 적용해 사람이 쓴 듯 자연스럽게 쓴다(사실·수치·고유명사는 그대로). 빈칸이나 확인이 필요한 부분은 추측하지 말고 표시해 사용자에게 묻는다.',
|
|
74
|
+
'완성본을 `save_cover_letter_version`으로 저장한다(기존 자기소개서면 cover_letter_id, 새로 만들면 title, 공고용이면 job_id, 변경 요약은 note, source=ai).',
|
|
75
|
+
'사용자가 원하면 `export_cover_letter`로 파일로 내보내고, 추가 수정 요청이 있으면 새 버전으로 다시 저장한다.',
|
|
76
|
+
'저장 결과를 쉬운 한국어로 알리고, 지원 상태 업데이트(`update_application_status`)나 면접 준비 등 다음 단계를 제안한다.',
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: 'manage_application_status',
|
|
81
|
+
title: '지원 상태 관리',
|
|
82
|
+
description:
|
|
83
|
+
'지원 진행 상황(8단계)을 업데이트하고 상태에 맞는 다음 행동을 제안하는 흐름.',
|
|
84
|
+
trigger:
|
|
85
|
+
'사용자가 "지원했어 / 서류 합격했어 / 면접 잡혔어 / 떨어졌어"처럼 진행 상황을 알리거나 상태 변경을 요청할 때.',
|
|
86
|
+
steps: [
|
|
87
|
+
'필요하면 `get_application_context` 또는 `get_job_posting`으로 대상 지원 건과 공고를 확인한다.',
|
|
88
|
+
'적절한 상태로 `update_application_status`를 호출한다. 8단계: draft(작성 중), planned(지원 예정), applied(지원 완료), document_passed(서류 합격), interview(면접 진행), final_passed(최종 합격), rejected(불합격), on_hold(보류). 변경 사유는 note에 남긴다.',
|
|
89
|
+
'변경 결과를 사용자에게 쉬운 한국어로 알린다.',
|
|
90
|
+
'상태에 맞는 다음 단계를 제안한다: applied → 결과 기다리기 안내, document_passed/interview → 면접 준비(prepare_interview) 제안, final_passed → 축하 및 마무리, rejected/on_hold → 회고 또는 다른 공고 탐색 제안.',
|
|
91
|
+
'필요하면 `open_application`으로 해당 지원 건을 열어 보여준다.',
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: 'prepare_interview',
|
|
96
|
+
title: '면접 준비',
|
|
97
|
+
description:
|
|
98
|
+
'서류 합격 이후 예상 질문·STAR 답변·1분 자기소개를 준비해 저장하는 흐름.',
|
|
99
|
+
trigger:
|
|
100
|
+
'지원 상태가 서류 합격(document_passed) 또는 면접 진행(interview)으로 바뀌었을 때, 또는 사용자가 "면접 준비 도와줘"라고 요청할 때.',
|
|
101
|
+
steps: [
|
|
102
|
+
'`get_application_context`(job_id 포함)를 호출해 공고, 프로필, 경력/프로젝트, 적합도 분석(강점·갭), 자기소개서를 가져온다.',
|
|
103
|
+
'공고와 사용자 경험을 바탕으로 예상 면접 질문(question, intent, followups, answer_outline)을 도출한다.',
|
|
104
|
+
'핵심 경험에 대한 STAR 가이드(question, situation, task, action, result)와 1분 자기소개(self_introduction) 초안을 작성한다. 실제 경험만 사용한다.',
|
|
105
|
+
'준비 내용을 `save_interview_prep`로 저장한다(job_id, questions, star_guides, self_introduction, notes).',
|
|
106
|
+
'준비 내용을 사용자에게 쉬운 한국어로 요약해 전달하고, 모의 면접/추가 질문 연습이나 상태 업데이트(`update_application_status`로 interview/final_passed)를 제안한다.',
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
];
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @careermate/workflows — public entry point.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the workflow data plus helpers to look up a single workflow and
|
|
5
|
+
* render it as readable Korean markdown (used in docs and MCP tool descriptions).
|
|
6
|
+
*/
|
|
7
|
+
export { WORKFLOWS } from './definitions.ts';
|
|
8
|
+
export type { WorkflowDefinition } from './definitions.ts';
|
|
9
|
+
|
|
10
|
+
import { WORKFLOWS, type WorkflowDefinition } from './definitions.ts';
|
|
11
|
+
|
|
12
|
+
/** Look up a single workflow by id. Returns undefined if the id is unknown. */
|
|
13
|
+
export function getWorkflow(id: string): WorkflowDefinition | undefined {
|
|
14
|
+
return WORKFLOWS.find((w) => w.id === id);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Render a workflow as a readable Korean markdown block. Used in documentation
|
|
19
|
+
* and as MCP tool descriptions so the AI can read the full step list inline.
|
|
20
|
+
* Returns undefined if the id is unknown.
|
|
21
|
+
*/
|
|
22
|
+
export function renderWorkflowMarkdown(id: string): string | undefined {
|
|
23
|
+
const wf = getWorkflow(id);
|
|
24
|
+
if (!wf) return undefined;
|
|
25
|
+
|
|
26
|
+
const lines: string[] = [];
|
|
27
|
+
lines.push(`# ${wf.title}`);
|
|
28
|
+
lines.push('');
|
|
29
|
+
lines.push(wf.description);
|
|
30
|
+
lines.push('');
|
|
31
|
+
lines.push(`**트리거:** ${wf.trigger}`);
|
|
32
|
+
lines.push('');
|
|
33
|
+
lines.push('## 단계');
|
|
34
|
+
wf.steps.forEach((step, i) => {
|
|
35
|
+
lines.push(`${i + 1}. ${step}`);
|
|
36
|
+
});
|
|
37
|
+
lines.push('');
|
|
38
|
+
return lines.join('\n');
|
|
39
|
+
}
|
package/scripts/init.ts
ADDED
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scripts/init.ts — CareerMate 자동 설치/연결.
|
|
3
|
+
*
|
|
4
|
+
* 비개발자도 명령 한 번(또는 AI 에이전트가 INSTALL.md를 따라가며)으로 끝나도록, AI 클라이언트의
|
|
5
|
+
* MCP 설정에 CareerMate 서버를 자동 등록하고 로컬 데이터 폴더를 준비한다. CareerMate 안에는
|
|
6
|
+
* LLM이 없으므로, 이 단계가 끝나면 사용자의 AI가 MCP로 로컬 데이터를 읽고 쓸 수 있다.
|
|
7
|
+
*
|
|
8
|
+
* v1이 지원하는 연결 대상(우선순위 순):
|
|
9
|
+
* - Claude Desktop → claude_desktop_config.json (JSON · mcpServers)
|
|
10
|
+
* - Claude Code → <프로젝트>/.mcp.json (JSON · mcpServers, project scope)
|
|
11
|
+
* - Codex CLI → ~/.codex/config.toml (TOML · [mcp_servers.careermate])
|
|
12
|
+
* - Cursor → ~/.cursor/mcp.json (JSON · mcpServers, 기타 MCP 클라이언트 호환)
|
|
13
|
+
*
|
|
14
|
+
* 사용:
|
|
15
|
+
* careermate init 감지되는 클라이언트에 등록 (기본: 로컬 실행 모드)
|
|
16
|
+
* careermate init --client claude-code 특정 클라이언트만 (claude | claude-code | codex | cursor | gemini | antigravity)
|
|
17
|
+
* careermate init --all-clients 감지 여부와 무관하게 지원하는 모든 클라이언트에 등록
|
|
18
|
+
* careermate init --npx MCP 실행을 npx 방식으로 등록(공개 배포/개발자용)
|
|
19
|
+
* careermate init --print 실제로 쓰지 않고 등록할 설정/명령만 출력
|
|
20
|
+
*
|
|
21
|
+
* 주의(메모리: careerflow-windows-tsx-exit-gotcha): top-level await + process.exit 조합은
|
|
22
|
+
* Windows tsx에서 종료코드를 오염시키므로, 동기 main()으로 두고 실패는 process.exitCode로만 표시한다.
|
|
23
|
+
*/
|
|
24
|
+
import os from 'node:os';
|
|
25
|
+
import fs from 'node:fs';
|
|
26
|
+
import path from 'node:path';
|
|
27
|
+
import { fileURLToPath } from 'node:url';
|
|
28
|
+
|
|
29
|
+
/** npm에 공개되는 패키지 이름 = MCP 서버 키로도 사용. */
|
|
30
|
+
const PKG = 'careermate';
|
|
31
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
32
|
+
|
|
33
|
+
type ConfigFormat = 'mcp-json' | 'codex-toml';
|
|
34
|
+
|
|
35
|
+
interface ClientTarget {
|
|
36
|
+
id: string;
|
|
37
|
+
label: string;
|
|
38
|
+
format: ConfigFormat;
|
|
39
|
+
configPath: string;
|
|
40
|
+
/** JSON 클라이언트 중 `"type": "stdio"`를 함께 써야 하는 경우(Claude Code). */
|
|
41
|
+
jsonType?: boolean;
|
|
42
|
+
/** project scope처럼 항상 적용 가능한 대상은 감지 없이 기본 포함. */
|
|
43
|
+
alwaysPresent?: boolean;
|
|
44
|
+
/** 설정 경로가 유동적인 대상(Antigravity)은 '실제 파일이 있을 때만' 감지해 추측 파일 생성을 막는다. */
|
|
45
|
+
requireFileForPresent?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface ServerEntry {
|
|
49
|
+
command: string;
|
|
50
|
+
args: string[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface WriteResult {
|
|
54
|
+
changed: boolean;
|
|
55
|
+
backedUp: string | null;
|
|
56
|
+
replacedExisting: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** 데이터 폴더(@careermate/db의 getDataDir와 동일 규칙). db 패키지를 끌어오지 않으려고 인라인. */
|
|
60
|
+
function ensureDataDir(): string {
|
|
61
|
+
const override = process.env.CAREERMATE_DATA_DIR?.trim();
|
|
62
|
+
const dir = override && override.length > 0 ? override : path.join(os.homedir(), '.careermate');
|
|
63
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
64
|
+
return dir;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function claudeDesktopConfigPath(): string {
|
|
68
|
+
const home = os.homedir();
|
|
69
|
+
if (process.platform === 'win32') {
|
|
70
|
+
const appData = process.env.APPDATA ?? path.join(home, 'AppData', 'Roaming');
|
|
71
|
+
return path.join(appData, 'Claude', 'claude_desktop_config.json');
|
|
72
|
+
}
|
|
73
|
+
if (process.platform === 'darwin') {
|
|
74
|
+
return path.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
75
|
+
}
|
|
76
|
+
return path.join(home, '.config', 'Claude', 'claude_desktop_config.json');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function cursorConfigPath(): string {
|
|
80
|
+
return path.join(os.homedir(), '.cursor', 'mcp.json');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Claude Code project scope 설정: 사용자가 `claude`를 띄우는 CareerMate 폴더(ROOT)에 둔다. */
|
|
84
|
+
function claudeCodeConfigPath(): string {
|
|
85
|
+
return path.join(ROOT, '.mcp.json');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** Codex CLI 글로벌 설정(CODEX_HOME 우선). */
|
|
89
|
+
function codexConfigPath(): string {
|
|
90
|
+
const home = process.env.CODEX_HOME?.trim();
|
|
91
|
+
const base = home && home.length > 0 ? home : path.join(os.homedir(), '.codex');
|
|
92
|
+
return path.join(base, 'config.toml');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Gemini CLI: `~/.gemini/settings.json` 의 mcpServers(Cursor 등과 동일 JSON 형식). */
|
|
96
|
+
function geminiConfigPath(): string {
|
|
97
|
+
return path.join(os.homedir(), '.gemini', 'settings.json');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Antigravity(Gemini CLI의 소비자용 후속, 2026-06-18 전환) MCP 설정.
|
|
102
|
+
* 출시 직후라 정확한 파일 경로가 커뮤니티 소스 간 갈려, 존재하는 후보를 우선 사용한다.
|
|
103
|
+
* requireFileForPresent로 '실제 파일이 있을 때만' 감지 → 기본 init은 추측 경로에 파일을 만들지 않는다
|
|
104
|
+
* (--all-clients / --client antigravity 일 때만 첫 후보에 생성). 스키마는 Gemini와 동일(mcpServers).
|
|
105
|
+
*/
|
|
106
|
+
function antigravityCandidates(): string[] {
|
|
107
|
+
const g = path.join(os.homedir(), '.gemini');
|
|
108
|
+
return [
|
|
109
|
+
path.join(g, 'config', 'mcp_config.json'),
|
|
110
|
+
path.join(g, 'antigravity', 'mcp_config.json'),
|
|
111
|
+
path.join(g, 'antigravity-cli', 'mcp_config.json'),
|
|
112
|
+
];
|
|
113
|
+
}
|
|
114
|
+
function antigravityConfigPath(): string {
|
|
115
|
+
return antigravityCandidates().find((p) => fs.existsSync(p)) ?? antigravityCandidates()[0];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function allTargets(): ClientTarget[] {
|
|
119
|
+
return [
|
|
120
|
+
{ id: 'claude', label: 'Claude Desktop', format: 'mcp-json', configPath: claudeDesktopConfigPath() },
|
|
121
|
+
{ id: 'claude-code', label: 'Claude Code', format: 'mcp-json', configPath: claudeCodeConfigPath(), jsonType: true, alwaysPresent: true },
|
|
122
|
+
{ id: 'codex', label: 'Codex CLI', format: 'codex-toml', configPath: codexConfigPath() },
|
|
123
|
+
{ id: 'cursor', label: 'Cursor (기타 MCP 클라이언트)', format: 'mcp-json', configPath: cursorConfigPath() },
|
|
124
|
+
{ id: 'gemini', label: 'Gemini CLI', format: 'mcp-json', configPath: geminiConfigPath() },
|
|
125
|
+
{ id: 'antigravity', label: 'Antigravity', format: 'mcp-json', configPath: antigravityConfigPath(), requireFileForPresent: true },
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 대상이 이 컴퓨터에 "있어 보이는가" — 설정 파일이나 그 부모 폴더가 존재하면 설치된 것으로 본다.
|
|
131
|
+
* 기본 `init`은 감지된 대상에만 써서 쓰지 않는 도구의 설정 파일을 만들지 않는다(임의 파일 생성 최소화).
|
|
132
|
+
*/
|
|
133
|
+
function isPresent(t: ClientTarget): boolean {
|
|
134
|
+
if (t.alwaysPresent) return true;
|
|
135
|
+
if (t.requireFileForPresent) return fs.existsSync(t.configPath);
|
|
136
|
+
return fs.existsSync(t.configPath) || fs.existsSync(path.dirname(t.configPath));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function pickTargets(which: string, forceAll: boolean): ClientTarget[] {
|
|
140
|
+
const all = allTargets();
|
|
141
|
+
if (which !== 'all') return all.filter((t) => t.id === which);
|
|
142
|
+
if (forceAll) return all;
|
|
143
|
+
return all.filter(isPresent);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* MCP 서버 실행 방법.
|
|
148
|
+
* - 기본(local): 현재 Node로 이 패키지의 bin을 직접 호출 → PATH/네트워크에 의존하지 않아
|
|
149
|
+
* 설치 파일 안에 동봉된 런타임에서도 그대로 동작.
|
|
150
|
+
* - --npx: npm 레지스트리에서 받아 실행(개발자/공개 배포용). npx 캐시 경로는 휘발성이라
|
|
151
|
+
* npx로 실행된 init은 자동으로 이 방식을 기본값으로 쓴다(절대경로가 곧 깨지는 것을 방지).
|
|
152
|
+
*/
|
|
153
|
+
function serverEntry(useNpx: boolean): ServerEntry {
|
|
154
|
+
if (useNpx) return { command: 'npx', args: ['-y', PKG, 'mcp'] };
|
|
155
|
+
return { command: process.execPath, args: [path.join(ROOT, 'bin', 'careermate.mjs'), 'mcp'] };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** 이 프로세스가 `npx`를 통해 실행됐는지 추정(휘발성 캐시에서 돔). */
|
|
159
|
+
function runningUnderNpx(): boolean {
|
|
160
|
+
const ua = process.env.npm_config_user_agent ?? '';
|
|
161
|
+
if (/\bnpx\b/.test(ua)) return true;
|
|
162
|
+
// npx 캐시 디렉터리 흔적(`_npx`)이 패키지 루트 경로에 있으면 npx 실행으로 본다.
|
|
163
|
+
return ROOT.split(path.sep).includes('_npx');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function readJsonObject(file: string): Record<string, unknown> {
|
|
167
|
+
try {
|
|
168
|
+
const raw = fs.readFileSync(file, 'utf8').trim();
|
|
169
|
+
if (!raw) return {};
|
|
170
|
+
const parsed = JSON.parse(raw);
|
|
171
|
+
return parsed && typeof parsed === 'object' ? (parsed as Record<string, unknown>) : {};
|
|
172
|
+
} catch {
|
|
173
|
+
return {};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function backupFile(file: string): string | null {
|
|
178
|
+
// 타임스탬프를 붙여 이전 백업을 덮어쓰지 않는다(원본을 영구 보존).
|
|
179
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
180
|
+
const dest = `${file}.careermate-backup-${stamp}`;
|
|
181
|
+
try {
|
|
182
|
+
fs.copyFileSync(file, dest);
|
|
183
|
+
return dest;
|
|
184
|
+
} catch {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Claude Desktop / Claude Code / Cursor 등 `mcpServers` JSON 형식. */
|
|
190
|
+
function writeMcpJson(target: ClientTarget, server: ServerEntry): WriteResult {
|
|
191
|
+
const existed = fs.existsSync(target.configPath);
|
|
192
|
+
const json = existed ? readJsonObject(target.configPath) : {};
|
|
193
|
+
const servers =
|
|
194
|
+
json.mcpServers && typeof json.mcpServers === 'object'
|
|
195
|
+
? (json.mcpServers as Record<string, unknown>)
|
|
196
|
+
: {};
|
|
197
|
+
|
|
198
|
+
const prev = servers[PKG];
|
|
199
|
+
const before = JSON.stringify(prev ?? null);
|
|
200
|
+
// 사용자가 careermate 항목에 직접 넣어둔 env(예: CAREERMATE_DATA_DIR)는 보존한다.
|
|
201
|
+
const next: Record<string, unknown> = {};
|
|
202
|
+
if (target.jsonType) next.type = 'stdio';
|
|
203
|
+
next.command = server.command;
|
|
204
|
+
next.args = server.args;
|
|
205
|
+
if (prev && typeof prev === 'object' && 'env' in (prev as Record<string, unknown>)) {
|
|
206
|
+
next.env = (prev as Record<string, unknown>).env;
|
|
207
|
+
}
|
|
208
|
+
servers[PKG] = next;
|
|
209
|
+
json.mcpServers = servers;
|
|
210
|
+
const changed = JSON.stringify(servers[PKG]) !== before;
|
|
211
|
+
const replacedExisting = prev != null && changed;
|
|
212
|
+
|
|
213
|
+
let backedUp: string | null = null;
|
|
214
|
+
if (existed && changed) backedUp = backupFile(target.configPath);
|
|
215
|
+
fs.mkdirSync(path.dirname(target.configPath), { recursive: true });
|
|
216
|
+
fs.writeFileSync(target.configPath, `${JSON.stringify(json, null, 2)}\n`, 'utf8');
|
|
217
|
+
return { changed, backedUp, replacedExisting };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** TOML 기본 문자열로 직렬화(역슬래시/따옴표 이스케이프). JSON 문자열 규칙과 호환된다. */
|
|
221
|
+
function tomlString(s: string): string {
|
|
222
|
+
return JSON.stringify(s);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Codex 설정의 careermate 블록(헤더 + command + args). 우리가 형태를 통제하므로 단순하다. */
|
|
226
|
+
function codexBlock(server: ServerEntry): string {
|
|
227
|
+
const args = server.args.map(tomlString).join(', ');
|
|
228
|
+
return [`[mcp_servers.${PKG}]`, `command = ${tomlString(server.command)}`, `args = [${args}]`].join('\n');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* 기존 config.toml에 careermate 블록을 안전하게 삽입/교체한다.
|
|
233
|
+
* - TOML 파서를 끌어오지 않고 텍스트로 처리하되, 우리가 만든 블록만 건드린다.
|
|
234
|
+
* - 블록 경계는 "다음 `[` 로 시작하는 테이블 헤더"까지 → `[mcp_servers.careermate.env]`
|
|
235
|
+
* 같은 사용자 하위 테이블은 보존된다.
|
|
236
|
+
*/
|
|
237
|
+
function upsertCodexBlock(raw: string, block: string): { next: string; found: boolean } {
|
|
238
|
+
const lines = raw.length ? raw.split(/\r?\n/) : [];
|
|
239
|
+
const headerRe = /^\s*\[\s*mcp_servers\s*\.\s*careermate\s*\]\s*$/;
|
|
240
|
+
const tableRe = /^\s*\[/;
|
|
241
|
+
const start = lines.findIndex((l) => headerRe.test(l));
|
|
242
|
+
|
|
243
|
+
if (start === -1) {
|
|
244
|
+
const trimmed = raw.replace(/\s+$/, '');
|
|
245
|
+
const next = trimmed.length ? `${trimmed}\n\n${block}\n` : `${block}\n`;
|
|
246
|
+
return { next, found: false };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
let end = lines.length;
|
|
250
|
+
for (let j = start + 1; j < lines.length; j++) {
|
|
251
|
+
if (tableRe.test(lines[j])) {
|
|
252
|
+
end = j;
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
const merged = [...lines.slice(0, start), ...block.split('\n'), ...lines.slice(end)];
|
|
257
|
+
let next = merged.join('\n');
|
|
258
|
+
if (!next.endsWith('\n')) next += '\n';
|
|
259
|
+
return { next, found: true };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** Codex CLI: `~/.codex/config.toml` 의 `[mcp_servers.careermate]` 테이블. */
|
|
263
|
+
function writeCodexToml(target: ClientTarget, server: ServerEntry): WriteResult {
|
|
264
|
+
const existed = fs.existsSync(target.configPath);
|
|
265
|
+
let raw = '';
|
|
266
|
+
if (existed) {
|
|
267
|
+
try {
|
|
268
|
+
raw = fs.readFileSync(target.configPath, 'utf8');
|
|
269
|
+
} catch {
|
|
270
|
+
raw = '';
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
const { next, found } = upsertCodexBlock(raw, codexBlock(server));
|
|
274
|
+
const changed = next.trim() !== raw.trim();
|
|
275
|
+
|
|
276
|
+
let backedUp: string | null = null;
|
|
277
|
+
if (existed && changed) backedUp = backupFile(target.configPath);
|
|
278
|
+
fs.mkdirSync(path.dirname(target.configPath), { recursive: true });
|
|
279
|
+
fs.writeFileSync(target.configPath, next, 'utf8');
|
|
280
|
+
return { changed, backedUp, replacedExisting: found && changed };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function writeTarget(target: ClientTarget, server: ServerEntry): WriteResult {
|
|
284
|
+
return target.format === 'codex-toml' ? writeCodexToml(target, server) : writeMcpJson(target, server);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function flagValue(argv: string[], name: string): string | undefined {
|
|
288
|
+
const i = argv.indexOf(name);
|
|
289
|
+
return i >= 0 && argv[i + 1] ? argv[i + 1] : undefined;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** 셸에 그대로 붙여넣을 수 있는 명령 문자열(공백 포함 인자는 따옴표). */
|
|
293
|
+
function shellJoin(parts: string[]): string {
|
|
294
|
+
return parts.map((p) => (/\s/.test(p) ? `"${p}"` : p)).join(' ');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function printConfigs(server: ServerEntry): void {
|
|
298
|
+
const cmd = [server.command, ...server.args];
|
|
299
|
+
console.log('CareerMate를 AI 클라이언트에 직접 연결하는 방법입니다.\n');
|
|
300
|
+
|
|
301
|
+
console.log('① Claude Desktop / Claude Code / Cursor / Gemini CLI / Antigravity — MCP 설정(mcpServers)에 추가:');
|
|
302
|
+
console.log(
|
|
303
|
+
JSON.stringify(
|
|
304
|
+
{ mcpServers: { [PKG]: { type: 'stdio', command: server.command, args: server.args } } },
|
|
305
|
+
null,
|
|
306
|
+
2,
|
|
307
|
+
),
|
|
308
|
+
);
|
|
309
|
+
console.log('\n · Claude Code는 위 내용을 프로젝트 루트의 .mcp.json 으로 저장하면 됩니다.');
|
|
310
|
+
console.log(' · Gemini CLI는 ~/.gemini/settings.json, Antigravity는 ~/.gemini/(config|antigravity|antigravity-cli)/mcp_config.json 의 mcpServers에 같은 블록을 넣으세요(type 키는 없어도 됨).');
|
|
311
|
+
console.log(` · 또는 명령으로: ${shellJoin(['claude', 'mcp', 'add', '--scope', 'project', '--transport', 'stdio', PKG, '--', ...cmd])}`);
|
|
312
|
+
|
|
313
|
+
console.log('\n② Codex CLI — ~/.codex/config.toml 에 추가:');
|
|
314
|
+
console.log(codexBlock(server));
|
|
315
|
+
console.log(`\n · 또는 명령으로: ${shellJoin(['codex', 'mcp', 'add', PKG, '--', ...cmd])}`);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function main(): void {
|
|
319
|
+
const argv = process.argv.slice(2);
|
|
320
|
+
const useNpx = argv.includes('--npx') || runningUnderNpx();
|
|
321
|
+
const printOnly = argv.includes('--print');
|
|
322
|
+
const forceAll = argv.includes('--all-clients');
|
|
323
|
+
const which = flagValue(argv, '--client') ?? 'all';
|
|
324
|
+
|
|
325
|
+
const server = serverEntry(useNpx);
|
|
326
|
+
|
|
327
|
+
if (printOnly) {
|
|
328
|
+
printConfigs(server);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
console.log('CareerMate 설치를 시작합니다…\n');
|
|
333
|
+
const dataDir = ensureDataDir();
|
|
334
|
+
console.log(`• 데이터 폴더 준비됨: ${dataDir}`);
|
|
335
|
+
|
|
336
|
+
const targets = pickTargets(which, forceAll);
|
|
337
|
+
let connected = 0;
|
|
338
|
+
const connectedIds = new Set<string>();
|
|
339
|
+
for (const t of targets) {
|
|
340
|
+
try {
|
|
341
|
+
const { changed, backedUp, replacedExisting } = writeTarget(t, server);
|
|
342
|
+
connected += 1;
|
|
343
|
+
connectedIds.add(t.id);
|
|
344
|
+
console.log(
|
|
345
|
+
`• ${t.label} 연결 ${changed ? '완료' : '이미 최신'}: ${t.configPath}` +
|
|
346
|
+
(replacedExisting ? `\n (기존 careermate 설정을 새 설정으로 교체했습니다)` : '') +
|
|
347
|
+
(backedUp ? `\n (기존 설정 백업: ${backedUp})` : ''),
|
|
348
|
+
);
|
|
349
|
+
} catch (e) {
|
|
350
|
+
console.log(`• ${t.label} 연결 실패: ${e instanceof Error ? e.message : e}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (connected === 0) {
|
|
355
|
+
console.log('\n연결할 AI 클라이언트를 찾지 못했습니다.');
|
|
356
|
+
console.log('특정 클라이언트를 지정하거나(--client claude|claude-code|codex|cursor|gemini|antigravity),');
|
|
357
|
+
console.log('설정을 직접 복사해 추가할 수 있어요: careermate init --print');
|
|
358
|
+
process.exitCode = 1;
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
console.log('\n✅ 거의 끝났습니다. 다음만 해주세요:');
|
|
363
|
+
console.log(' 1) 연결한 AI 클라이언트를 완전히 종료했다가 다시 켜기');
|
|
364
|
+
if (connectedIds.has('claude-code')) {
|
|
365
|
+
console.log(' (Claude Code는 프로젝트의 .mcp.json을 처음 띄울 때 1회 "승인"을 물어봅니다 — 승인해 주세요.)');
|
|
366
|
+
}
|
|
367
|
+
console.log(' 2) AI에게: "get_onboarding_status 호출해서 연결됐는지 확인해줘"');
|
|
368
|
+
console.log(' 3) 내 데이터 대시보드 열기: careermate start');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
main();
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"lib": ["ES2023"],
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"strict": true,
|
|
9
|
+
"noUncheckedIndexedAccess": false,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"forceConsistentCasingInFileNames": true,
|
|
14
|
+
"allowImportingTsExtensions": true,
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
"baseUrl": ".",
|
|
17
|
+
"paths": {
|
|
18
|
+
"@careermate/shared": ["packages/shared/src/index.ts"],
|
|
19
|
+
"@careermate/db": ["packages/db/src/index.ts"],
|
|
20
|
+
"@careermate/core": ["packages/core/src/index.ts"],
|
|
21
|
+
"@careermate/mcp-tools": ["packages/mcp-tools/src/index.ts"],
|
|
22
|
+
"@careermate/exporters": ["packages/exporters/src/index.ts"],
|
|
23
|
+
"@careermate/parsers": ["packages/parsers/src/index.ts"],
|
|
24
|
+
"@careermate/prompts": ["packages/prompts/src/index.ts"],
|
|
25
|
+
"@careermate/workflows": ["packages/workflows/src/index.ts"],
|
|
26
|
+
"@careermate/knowledge": ["packages/knowledge/src/index.ts"]
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"include": ["packages/**/*.ts", "apps/**/*.ts", "scripts/**/*.ts"],
|
|
30
|
+
"//exclude": "scripts/shots*.ts는 Playwright 브라우저 컨텍스트(window/document)·임시 시드용 마케팅 도구라 타입검사 제외(배포물 아님).",
|
|
31
|
+
"exclude": ["node_modules", "scripts/shots.ts", "scripts/shots-seed.ts"]
|
|
32
|
+
}
|