careermate 0.1.2 → 0.1.4
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/LICENSE +21 -0
- package/README.md +3 -1
- package/apps/web/public/pages/documents.js +7 -2
- package/apps/web/public/pages/home.js +4 -7
- package/apps/web/public/pages/jobs.js +1 -5
- package/apps/web/public/pages/profile.js +3 -3
- package/apps/web/public/styles.css +7 -2
- package/dist/doctor.mjs +112 -90
- package/dist/install-page/analytics.js +97 -0
- package/dist/install-page/index.html +77 -56
- package/dist/install-page/og.png +0 -0
- package/dist/install-page/robots.txt +4 -0
- package/dist/install-page/sitemap.xml +21 -0
- package/dist/install-page/start.html +13 -10
- package/dist/install-page/style.css +75 -40
- package/dist/mcp.mjs +112 -90
- package/dist/migrate.mjs +95 -82
- package/dist/public/pages/documents.js +7 -2
- package/dist/public/pages/home.js +4 -7
- package/dist/public/pages/jobs.js +1 -5
- package/dist/public/pages/profile.js +3 -3
- package/dist/public/styles.css +7 -2
- package/dist/web.mjs +113 -86
- package/docs/FAQ.md +101 -0
- package/docs/career-os/README.md +76 -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 +11 -3
- package/packages/db/src/repositories.ts +18 -1
- package/packages/mcp-tools/src/tools.ts +5 -5
- package/packages/parsers/src/documents.ts +13 -1
- package/packages/shared/src/schemas.ts +115 -81
- package/scripts/build-dist.mjs +7 -1
- package/scripts/gen-og.mjs +1 -1
- package/scripts/test.ts +25 -0
- package/scripts/ui-smoke.ts +32 -4
- package/dist/install-page/.omc/state/agent-replay-55a58c0d-bb6e-43ea-8a05-4b6c93b80b34.jsonl +0 -12
- package/dist/install-page/.omc/state/idle-notif-cooldown.json +0 -3
- package/dist/install-page/.omc/state/sessions/55a58c0d-bb6e-43ea-8a05-4b6c93b80b34/mission-state.json +0 -147
- package/dist/install-page/.omc/state/sessions/55a58c0d-bb6e-43ea-8a05-4b6c93b80b34/pre-tool-advisory-throttle.json +0 -22
- package/dist/install-page/.omc/state/sessions/55a58c0d-bb6e-43ea-8a05-4b6c93b80b34/subagent-tracking-state.json +0 -44
- package/docs/ARCHITECTURE.md +0 -208
- package/docs/CHANGES_V1.md +0 -103
- package/docs/DATA_MODEL.md +0 -460
- package/docs/DECISIONS.md +0 -277
- package/docs/DEMO.md +0 -242
- package/docs/ROADMAP.md +0 -135
- package/docs/RUNTIME_DISTRIBUTION_RESEARCH.md +0 -135
- package/docs/TODO.md +0 -57
- package/docs/UX_NOTES.md +0 -247
- package/docs/superpowers/specs/2026-06-16-demo-dashboard-deploy-design.md +0 -64
|
@@ -23,6 +23,7 @@ export async function render(ctx) {
|
|
|
23
23
|
root.append(el('div', { class: 'tabs' }, tabCover, tabDocs), panel);
|
|
24
24
|
|
|
25
25
|
let query = '';
|
|
26
|
+
let searchInput = null;
|
|
26
27
|
function selectTab(next) {
|
|
27
28
|
tab = next;
|
|
28
29
|
query = '';
|
|
@@ -43,9 +44,12 @@ export async function render(ctx) {
|
|
|
43
44
|
if (!hide) shown += 1;
|
|
44
45
|
}
|
|
45
46
|
const existing = panel.querySelector('.search-empty');
|
|
47
|
+
if (existing) existing.remove();
|
|
46
48
|
if (q && cards.length && shown === 0) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
panel.append(el('div', { class: 'search-empty empty' },
|
|
50
|
+
el('p', { class: 'muted', style: { margin: '0 0 12px' } }, `‘${query.trim()}’에 맞는 항목이 없어요.`),
|
|
51
|
+
Btn('필터 초기화', { sm: true, variant: 'ghost', onClick: () => { query = ''; if (searchInput) searchInput.value = ''; applyFilter(); } })));
|
|
52
|
+
}
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
async function reload() {
|
|
@@ -70,6 +74,7 @@ export async function render(ctx) {
|
|
|
70
74
|
function setActions() {
|
|
71
75
|
const search = Input({ type: 'search', placeholder: tab === 'cover' ? '자기소개서 검색' : '문서 검색', value: query, attrs: { 'aria-label': '검색' } });
|
|
72
76
|
search.classList.add('input--inline');
|
|
77
|
+
searchInput = search;
|
|
73
78
|
search.addEventListener('input', () => { query = search.value; applyFilter(); });
|
|
74
79
|
const createBtn = tab === 'cover'
|
|
75
80
|
? Btn('새 자기소개서', { icon: 'plus', variant: 'primary', onClick: () => openCoverCreate(reload) })
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// the 시작하기 card. Every empty state is ONE screen-specific sentence; never repeat
|
|
5
5
|
// the same "AI에게 보내면 저장됩니다" instruction across surfaces.
|
|
6
6
|
import {
|
|
7
|
-
el, get, icon, navigate, Card, Badge, Btn,
|
|
7
|
+
el, get, icon, navigate, Card, Badge, Btn, ListRow, CheckRow,
|
|
8
8
|
Stat, PageHead, fmtDate, fmtRelative, scoreClass, STATUS_COLOR, statusColor, mount,
|
|
9
9
|
} from '/lib.js';
|
|
10
10
|
|
|
@@ -59,7 +59,7 @@ function Greeting(name, s, firstRun) {
|
|
|
59
59
|
let focus;
|
|
60
60
|
if (firstRun) focus = '시작해 볼까요 — 먼저 기본 정보를 채워 주세요.';
|
|
61
61
|
else if (s.interview_todo.length) focus = `면접 준비가 필요한 지원 ${s.interview_todo.length}건이 있어요.`;
|
|
62
|
-
else if (s.in_progress.length) focus = `진행 중인 지원 ${s.in_progress.length}
|
|
62
|
+
else if (s.in_progress.length) focus = `진행 중인 지원 ${s.in_progress.length}건이 있어요.`;
|
|
63
63
|
else if (s.counts.jobs) focus = `저장된 공고 ${s.counts.jobs}건 · 새 적합도 분석을 받아보세요.`;
|
|
64
64
|
else focus = '오늘도 한 걸음 나아가 볼까요.';
|
|
65
65
|
return PageHead({ title: name ? `${name}님, 안녕하세요` : '환영합니다', desc: focus });
|
|
@@ -87,7 +87,7 @@ function GettingStarted(o) {
|
|
|
87
87
|
el('div', { class: 'callout__body' },
|
|
88
88
|
'공고 분석·자기소개서·면접 준비는 평소 쓰던 AI에게 말로 시키면, 결과가 이 대시보드에 쌓여요.'))),
|
|
89
89
|
el('div', { class: 'progress' }, el('div', { class: 'progress__bar', style: { width: `${o.profile_completeness}%` } })),
|
|
90
|
-
el('p', { class: 'text-secondary text-sm
|
|
90
|
+
el('p', { class: 'text-secondary text-sm mt-3 mb-2' },
|
|
91
91
|
'완성할수록 AI의 적합도 분석과 자기소개서 품질이 좋아져요.'),
|
|
92
92
|
el('div', {}, ...steps.map((it) => CheckRow({ done: it.done, label: it.label, onClick: () => navigate(it.to) }))),
|
|
93
93
|
],
|
|
@@ -201,10 +201,7 @@ function RecentJobs(jobs) {
|
|
|
201
201
|
if (!jobs.length) {
|
|
202
202
|
return Card({
|
|
203
203
|
title: '최근 공고',
|
|
204
|
-
body:
|
|
205
|
-
iconName: 'briefcase', title: '저장된 공고가 아직 없어요',
|
|
206
|
-
action: Btn('공고 추가', { icon: 'plus', variant: 'ghost', onClick: () => navigate('/jobs') }),
|
|
207
|
-
}),
|
|
204
|
+
body: el('p', { class: 'muted', style: { margin: 0 } }, '저장된 공고가 아직 없어요. 새 공고를 추가해 보세요.'),
|
|
208
205
|
});
|
|
209
206
|
}
|
|
210
207
|
const rows = jobs.map((j) => ListRow({
|
|
@@ -365,11 +365,7 @@ function FitCard(fit) {
|
|
|
365
365
|
if (!fit) {
|
|
366
366
|
return Card({
|
|
367
367
|
title: '적합도 분석',
|
|
368
|
-
body:
|
|
369
|
-
iconName: 'target',
|
|
370
|
-
title: '적합도 분석 전이에요',
|
|
371
|
-
body: '이 공고와 내 프로필을 비교한 분석 결과가 여기에 표시됩니다.',
|
|
372
|
-
}),
|
|
368
|
+
body: el('p', { class: 'muted', style: { margin: 0 } }, '아직 분석 전이에요. 이 공고와 내 프로필을 비교한 결과가 여기에 표시됩니다.'),
|
|
373
369
|
});
|
|
374
370
|
}
|
|
375
371
|
|
|
@@ -264,7 +264,7 @@ function editWriting(p, reload) {
|
|
|
264
264
|
/* ---------------------------------------------------- 4. 경력 (Experiences) */
|
|
265
265
|
|
|
266
266
|
function ExperiencesCard(items, reload) {
|
|
267
|
-
const add = Btn('추가', { icon: 'plus', sm: true, variant: 'ghost', onClick: () => editExperience(null, reload) });
|
|
267
|
+
const add = Btn('경력 추가', { icon: 'plus', sm: true, variant: 'ghost', onClick: () => editExperience(null, reload) });
|
|
268
268
|
|
|
269
269
|
if (!items.length) {
|
|
270
270
|
return Card({
|
|
@@ -368,7 +368,7 @@ function editExperience(x, reload) {
|
|
|
368
368
|
/* ----------------------------------------------------- 5. 프로젝트 (Projects) */
|
|
369
369
|
|
|
370
370
|
function ProjectsCard(items, reload) {
|
|
371
|
-
const add = Btn('추가', { icon: 'plus', sm: true, variant: 'ghost', onClick: () => editProject(null, reload) });
|
|
371
|
+
const add = Btn('프로젝트 추가', { icon: 'plus', sm: true, variant: 'ghost', onClick: () => editProject(null, reload) });
|
|
372
372
|
|
|
373
373
|
if (!items.length) {
|
|
374
374
|
return Card({
|
|
@@ -469,7 +469,7 @@ function editProject(x, reload) {
|
|
|
469
469
|
/* -------------------------------------------------------- 6. 기술 (Skills) */
|
|
470
470
|
|
|
471
471
|
function SkillsCard(items, reload) {
|
|
472
|
-
const add = Btn('추가', { icon: 'plus', sm: true, variant: 'ghost', onClick: () => editSkill(null, reload) });
|
|
472
|
+
const add = Btn('기술 추가', { icon: 'plus', sm: true, variant: 'ghost', onClick: () => editSkill(null, reload) });
|
|
473
473
|
|
|
474
474
|
if (!items.length) {
|
|
475
475
|
return Card({
|
package/dist/public/styles.css
CHANGED
|
@@ -403,7 +403,9 @@ h1,h2,h3,h4 { margin: 0; font-weight: 650; letter-spacing: -0.01em; }
|
|
|
403
403
|
background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 22px 26px; max-height: 60vh; overflow-y: auto; }
|
|
404
404
|
.kv { display: grid; grid-template-columns: 132px 1fr; gap: 8px 16px; }
|
|
405
405
|
.kv dt { color: var(--text-tertiary); font-size: 13px; }
|
|
406
|
-
|
|
406
|
+
/* min-width:0 overrides a grid item's default min-width:auto so the 1fr column
|
|
407
|
+
can shrink below its content — otherwise a long path/value overflows the grid. */
|
|
408
|
+
.kv dd { margin: 0; min-width: 0; }
|
|
407
409
|
|
|
408
410
|
/* ----------------------------------------------------------------- modal */
|
|
409
411
|
.modal-root { position: fixed; inset: 0; z-index: 60; display: none; }
|
|
@@ -450,7 +452,10 @@ h1,h2,h3,h4 { margin: 0; font-weight: 650; letter-spacing: -0.01em; }
|
|
|
450
452
|
.between { justify-content: space-between; } .center { align-items: center; } .wrap { flex-wrap: wrap; }
|
|
451
453
|
.ml-auto { margin-left: auto; } .mt-2{margin-top:8px;} .mt-3{margin-top:12px;} .mt-4{margin-top:16px;} .mb-2{margin-bottom:8px;} .mb-3{margin-bottom:12px;} .mb-4{margin-bottom:16px;}
|
|
452
454
|
.tnum { font-variant-numeric: tabular-nums; }
|
|
453
|
-
|
|
455
|
+
/* min-width:0 lets a truncating element actually shrink inside a flex row;
|
|
456
|
+
without it a long unbreakable value (e.g. a data-folder path) forces the
|
|
457
|
+
flex item to its content width and overflows the viewport on narrow screens. */
|
|
458
|
+
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; min-width: 0; }
|
|
454
459
|
.divider { height: 1px; background: var(--border); margin: 16px 0; }
|
|
455
460
|
.skeleton { background: linear-gradient(90deg, var(--bg-sunken) 25%, var(--surface-hover) 37%, var(--bg-sunken) 63%); background-size: 400% 100%; animation: shimmer 1.4s infinite; border-radius: 6px; }
|
|
456
461
|
@keyframes shimmer { 0% { background-position: 100% 0; } 100% { background-position: 0 0; } }
|
package/dist/web.mjs
CHANGED
|
@@ -43892,29 +43892,42 @@ function isSafeUrl(u) {
|
|
|
43892
43892
|
return !/^[a-z][a-z0-9+.-]*:/i.test(s) || /^(https?:|mailto:)/i.test(s);
|
|
43893
43893
|
}
|
|
43894
43894
|
var UNSAFE_URL_MSG = "javascript: \uAC19\uC740 \uC704\uD5D8\uD55C \uB9C1\uD06C\uB294 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. http(s) \uB9C1\uD06C\uB97C \uC0AC\uC6A9\uD558\uC138\uC694.";
|
|
43895
|
-
var
|
|
43896
|
-
var
|
|
43895
|
+
var MAX_URL = 2e3;
|
|
43896
|
+
var SafeUrl = external_exports.string().min(1).max(MAX_URL).refine(isSafeUrl, { message: UNSAFE_URL_MSG });
|
|
43897
|
+
var SafeUrlOptional = external_exports.string().max(MAX_URL).optional().refine((u) => u == null || u === "" || isSafeUrl(u), { message: UNSAFE_URL_MSG });
|
|
43898
|
+
var MAX_LINE = 500;
|
|
43899
|
+
var MAX_NOTE = 1e4;
|
|
43900
|
+
var MAX_BODY = 2e5;
|
|
43901
|
+
var MAX_ITEM = 2e3;
|
|
43902
|
+
var MAX_ITEMS = 500;
|
|
43903
|
+
var EMPTY_MSG = "\uB0B4\uC6A9\uC774 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4(\uACF5\uBC31\uB9CC\uC73C\uB85C\uB294 \uC800\uC7A5\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4).";
|
|
43904
|
+
var reqLine = external_exports.string().trim().min(1).max(MAX_LINE);
|
|
43905
|
+
var optLine = external_exports.string().max(MAX_LINE).optional();
|
|
43906
|
+
var optNote = external_exports.string().max(MAX_NOTE).optional();
|
|
43907
|
+
var optBody = external_exports.string().max(MAX_BODY).optional();
|
|
43908
|
+
var reqBody = external_exports.string().max(MAX_BODY).refine((s) => s.trim().length > 0, { message: EMPTY_MSG });
|
|
43909
|
+
var strList = external_exports.array(external_exports.string().max(MAX_ITEM)).max(MAX_ITEMS);
|
|
43897
43910
|
var baseRecord = {
|
|
43898
43911
|
id,
|
|
43899
43912
|
created_at: isoDateTime,
|
|
43900
43913
|
updated_at: isoDateTime
|
|
43901
43914
|
};
|
|
43902
43915
|
var LinkSchema = external_exports.object({
|
|
43903
|
-
label:
|
|
43916
|
+
label: reqLine,
|
|
43904
43917
|
url: SafeUrl
|
|
43905
43918
|
});
|
|
43906
43919
|
var ProfileInputSchema = external_exports.object({
|
|
43907
|
-
name:
|
|
43908
|
-
email:
|
|
43909
|
-
phone:
|
|
43910
|
-
location:
|
|
43911
|
-
headline: external_exports.string().optional().describe("\uD55C \uC904 \uC18C\uAC1C / \uC9C1\uBB34 \uD0C0\uC774\uD2C0"),
|
|
43912
|
-
summary: external_exports.string().optional().describe("\uC790\uAE30\uC18C\uAC1C \uC694\uC57D"),
|
|
43913
|
-
desired_roles:
|
|
43914
|
-
desired_conditions: external_exports.string().optional().describe("\uD76C\uB9DD \uADFC\uBB34 \uC870\uAC74 (\uC5F0\uBD09, \uC9C0\uC5ED, \uADFC\uBB34\uD615\uD0DC \uB4F1)"),
|
|
43915
|
-
preferred_tone: external_exports.string().optional().describe("\uC790\uAE30\uC18C\uAC1C\uC11C \uC120\uD638 \uBB38\uCCB4 (\uC608: \uB2F4\uBC31\uD558\uACE0 \uAD6C\uCCB4\uC801)"),
|
|
43916
|
-
emphasis_points:
|
|
43917
|
-
links: external_exports.array(LinkSchema).optional().describe("\uD3EC\uD2B8\uD3F4\uB9AC\uC624/\uAE43\uD5C8\uBE0C/\uB9C1\uD06C\uB4DC\uC778 \uB4F1")
|
|
43920
|
+
name: reqLine.optional(),
|
|
43921
|
+
email: optLine,
|
|
43922
|
+
phone: optLine,
|
|
43923
|
+
location: optLine,
|
|
43924
|
+
headline: external_exports.string().max(MAX_LINE).optional().describe("\uD55C \uC904 \uC18C\uAC1C / \uC9C1\uBB34 \uD0C0\uC774\uD2C0"),
|
|
43925
|
+
summary: external_exports.string().max(MAX_NOTE).optional().describe("\uC790\uAE30\uC18C\uAC1C \uC694\uC57D"),
|
|
43926
|
+
desired_roles: strList.optional().describe("\uD76C\uB9DD \uC9C1\uBB34"),
|
|
43927
|
+
desired_conditions: external_exports.string().max(MAX_NOTE).optional().describe("\uD76C\uB9DD \uADFC\uBB34 \uC870\uAC74 (\uC5F0\uBD09, \uC9C0\uC5ED, \uADFC\uBB34\uD615\uD0DC \uB4F1)"),
|
|
43928
|
+
preferred_tone: external_exports.string().max(MAX_LINE).optional().describe("\uC790\uAE30\uC18C\uAC1C\uC11C \uC120\uD638 \uBB38\uCCB4 (\uC608: \uB2F4\uBC31\uD558\uACE0 \uAD6C\uCCB4\uC801)"),
|
|
43929
|
+
emphasis_points: strList.optional().describe("\uAC15\uC870\uD558\uACE0 \uC2F6\uC740 \uD575\uC2EC \uD3EC\uC778\uD2B8"),
|
|
43930
|
+
links: external_exports.array(LinkSchema).max(MAX_ITEMS).optional().describe("\uD3EC\uD2B8\uD3F4\uB9AC\uC624/\uAE43\uD5C8\uBE0C/\uB9C1\uD06C\uB4DC\uC778 \uB4F1")
|
|
43918
43931
|
});
|
|
43919
43932
|
var ProfileRecordSchema = external_exports.object({
|
|
43920
43933
|
...baseRecord,
|
|
@@ -43931,15 +43944,15 @@ var ProfileRecordSchema = external_exports.object({
|
|
|
43931
43944
|
links: external_exports.array(LinkSchema)
|
|
43932
43945
|
});
|
|
43933
43946
|
var ExperienceInputSchema = external_exports.object({
|
|
43934
|
-
company:
|
|
43935
|
-
role:
|
|
43936
|
-
employment_type: external_exports.string().optional().describe("\uC815\uADDC\uC9C1/\uACC4\uC57D\uC9C1/\uC778\uD134/\uD504\uB9AC\uB79C\uC11C \uB4F1"),
|
|
43937
|
-
start_date: external_exports.string().optional().describe("YYYY-MM \uB610\uB294 YYYY-MM-DD"),
|
|
43938
|
-
end_date: external_exports.string().optional().describe("\uC7AC\uC9C1 \uC911\uC774\uBA74 \uBE44\uC6CC\uB450\uAE30"),
|
|
43947
|
+
company: reqLine,
|
|
43948
|
+
role: optLine,
|
|
43949
|
+
employment_type: external_exports.string().max(MAX_LINE).optional().describe("\uC815\uADDC\uC9C1/\uACC4\uC57D\uC9C1/\uC778\uD134/\uD504\uB9AC\uB79C\uC11C \uB4F1"),
|
|
43950
|
+
start_date: external_exports.string().max(MAX_LINE).optional().describe("YYYY-MM \uB610\uB294 YYYY-MM-DD"),
|
|
43951
|
+
end_date: external_exports.string().max(MAX_LINE).optional().describe("\uC7AC\uC9C1 \uC911\uC774\uBA74 \uBE44\uC6CC\uB450\uAE30"),
|
|
43939
43952
|
is_current: external_exports.boolean().optional(),
|
|
43940
|
-
description:
|
|
43941
|
-
achievements:
|
|
43942
|
-
tech:
|
|
43953
|
+
description: optBody,
|
|
43954
|
+
achievements: strList.optional().describe("\uC131\uACFC/\uC5C5\uC801 (\uC815\uB7C9 \uC9C0\uD45C \uAD8C\uC7A5)"),
|
|
43955
|
+
tech: strList.optional().describe("\uC0AC\uC6A9 \uAE30\uC220/\uB3C4\uAD6C"),
|
|
43943
43956
|
order_index: external_exports.number().optional()
|
|
43944
43957
|
});
|
|
43945
43958
|
var ExperienceRecordSchema = external_exports.object({
|
|
@@ -43956,14 +43969,14 @@ var ExperienceRecordSchema = external_exports.object({
|
|
|
43956
43969
|
order_index: external_exports.number()
|
|
43957
43970
|
});
|
|
43958
43971
|
var ProjectInputSchema = external_exports.object({
|
|
43959
|
-
name:
|
|
43960
|
-
role:
|
|
43961
|
-
description:
|
|
43962
|
-
highlights:
|
|
43963
|
-
tech:
|
|
43972
|
+
name: reqLine,
|
|
43973
|
+
role: optLine,
|
|
43974
|
+
description: optBody,
|
|
43975
|
+
highlights: strList.optional(),
|
|
43976
|
+
tech: strList.optional(),
|
|
43964
43977
|
url: SafeUrlOptional,
|
|
43965
|
-
start_date:
|
|
43966
|
-
end_date:
|
|
43978
|
+
start_date: optLine,
|
|
43979
|
+
end_date: optLine,
|
|
43967
43980
|
order_index: external_exports.number().optional()
|
|
43968
43981
|
});
|
|
43969
43982
|
var ProjectRecordSchema = external_exports.object({
|
|
@@ -43979,9 +43992,9 @@ var ProjectRecordSchema = external_exports.object({
|
|
|
43979
43992
|
order_index: external_exports.number()
|
|
43980
43993
|
});
|
|
43981
43994
|
var SkillInputSchema = external_exports.object({
|
|
43982
|
-
name:
|
|
43983
|
-
category: external_exports.string().optional().describe("\uC5B8\uC5B4/\uD504\uB808\uC784\uC6CC\uD06C/\uD234/\uC18C\uD504\uD2B8\uC2A4\uD0AC \uB4F1"),
|
|
43984
|
-
level: external_exports.string().optional().describe("\uC0C1/\uC911/\uD558 \uB610\uB294 \uC790\uC720 \uC11C\uC220"),
|
|
43995
|
+
name: reqLine,
|
|
43996
|
+
category: external_exports.string().max(MAX_LINE).optional().describe("\uC5B8\uC5B4/\uD504\uB808\uC784\uC6CC\uD06C/\uD234/\uC18C\uD504\uD2B8\uC2A4\uD0AC \uB4F1"),
|
|
43997
|
+
level: external_exports.string().max(MAX_LINE).optional().describe("\uC0C1/\uC911/\uD558 \uB610\uB294 \uC790\uC720 \uC11C\uC220"),
|
|
43985
43998
|
years: external_exports.number().optional(),
|
|
43986
43999
|
order_index: external_exports.number().optional()
|
|
43987
44000
|
});
|
|
@@ -43995,11 +44008,11 @@ var SkillRecordSchema = external_exports.object({
|
|
|
43995
44008
|
});
|
|
43996
44009
|
var DocumentInputSchema = external_exports.object({
|
|
43997
44010
|
kind: external_exports.enum(DOCUMENT_KINDS),
|
|
43998
|
-
title:
|
|
43999
|
-
content:
|
|
44011
|
+
title: reqLine,
|
|
44012
|
+
content: reqBody.describe("Markdown \uB610\uB294 \uC77C\uBC18 \uD14D\uC2A4\uD2B8 \uBCF8\uBB38"),
|
|
44000
44013
|
source: external_exports.enum(CONTENT_SOURCES).optional(),
|
|
44001
44014
|
is_primary: external_exports.boolean().optional().describe("\uB300\uD45C \uBB38\uC11C \uC5EC\uBD80"),
|
|
44002
|
-
tags:
|
|
44015
|
+
tags: strList.optional()
|
|
44003
44016
|
});
|
|
44004
44017
|
var DocumentRecordSchema = external_exports.object({
|
|
44005
44018
|
...baseRecord,
|
|
@@ -44011,19 +44024,19 @@ var DocumentRecordSchema = external_exports.object({
|
|
|
44011
44024
|
tags: external_exports.array(external_exports.string())
|
|
44012
44025
|
});
|
|
44013
44026
|
var CoverLetterInputSchema = external_exports.object({
|
|
44014
|
-
title:
|
|
44015
|
-
job_id:
|
|
44027
|
+
title: reqLine,
|
|
44028
|
+
job_id: optLine.describe("\uD2B9\uC815 \uACF5\uACE0\uC5D0 \uC5F0\uACB0\uD560 \uACBD\uC6B0"),
|
|
44016
44029
|
is_primary: external_exports.boolean().optional(),
|
|
44017
|
-
content:
|
|
44030
|
+
content: optBody.describe("\uCD08\uAE30 \uBC84\uC804 \uBCF8\uBB38 (\uC788\uC73C\uBA74 v1\uB85C \uC800\uC7A5)"),
|
|
44018
44031
|
source: external_exports.enum(CONTENT_SOURCES).optional(),
|
|
44019
|
-
note:
|
|
44032
|
+
note: optNote.describe("\uCD08\uAE30 \uBC84\uC804 \uBA54\uBAA8")
|
|
44020
44033
|
});
|
|
44021
44034
|
var CoverLetterVersionInputSchema = external_exports.object({
|
|
44022
|
-
cover_letter_id:
|
|
44023
|
-
title: external_exports.string().optional().describe("\uC0C8 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uB9CC\uB4E4 \uB54C \uC81C\uBAA9"),
|
|
44024
|
-
job_id:
|
|
44025
|
-
content:
|
|
44026
|
-
note:
|
|
44035
|
+
cover_letter_id: optLine.describe("\uAE30\uC874 \uC790\uAE30\uC18C\uAC1C\uC11C\uC5D0 \uC0C8 \uBC84\uC804 \uCD94\uAC00 \uC2DC"),
|
|
44036
|
+
title: external_exports.string().max(MAX_LINE).optional().describe("\uC0C8 \uC790\uAE30\uC18C\uAC1C\uC11C\uB97C \uB9CC\uB4E4 \uB54C \uC81C\uBAA9"),
|
|
44037
|
+
job_id: optLine,
|
|
44038
|
+
content: reqBody,
|
|
44039
|
+
note: optNote.describe("\uC774 \uBC84\uC804\uC758 \uBCC0\uACBD \uC694\uC57D (\uC608: \uC9C0\uC6D0\uB3D9\uAE30 \uBCF4\uAC15)"),
|
|
44027
44040
|
source: external_exports.enum(CONTENT_SOURCES).optional(),
|
|
44028
44041
|
set_current: external_exports.boolean().optional().describe("\uC774 \uBC84\uC804\uC744 \uD604\uC7AC \uBC84\uC804\uC73C\uB85C \uC9C0\uC815 (\uAE30\uBCF8 true)")
|
|
44029
44042
|
});
|
|
@@ -44047,16 +44060,16 @@ var CoverLetterRecordSchema = external_exports.object({
|
|
|
44047
44060
|
versions: external_exports.array(CoverLetterVersionRecordSchema).optional()
|
|
44048
44061
|
});
|
|
44049
44062
|
var JobInputSchema = external_exports.object({
|
|
44050
|
-
company:
|
|
44051
|
-
position:
|
|
44063
|
+
company: reqLine,
|
|
44064
|
+
position: reqLine.describe("\uC9C1\uBB34/\uD3EC\uC9C0\uC158\uBA85"),
|
|
44052
44065
|
url: SafeUrlOptional,
|
|
44053
|
-
location:
|
|
44054
|
-
employment_type:
|
|
44055
|
-
description:
|
|
44056
|
-
requirements:
|
|
44057
|
-
keywords:
|
|
44058
|
-
deadline: external_exports.string().optional().describe("\uB9C8\uAC10\uC77C YYYY-MM-DD"),
|
|
44059
|
-
source: external_exports.string().optional().describe("\uCD9C\uCC98 (\uC0AC\uB78C\uC778/\uC6D0\uD2F0\uB4DC/\uC9C1\uC811 \uC785\uB825 \uB4F1)")
|
|
44066
|
+
location: optLine,
|
|
44067
|
+
employment_type: optLine,
|
|
44068
|
+
description: optBody.describe("\uACF5\uACE0 \uC6D0\uBB38 \uB610\uB294 \uC815\uB9AC\uB41C \uD14D\uC2A4\uD2B8"),
|
|
44069
|
+
requirements: strList.optional().describe("\uC790\uACA9\uC694\uAC74/\uC6B0\uB300\uC0AC\uD56D \uD575\uC2EC"),
|
|
44070
|
+
keywords: strList.optional().describe("\uD575\uC2EC \uD0A4\uC6CC\uB4DC"),
|
|
44071
|
+
deadline: external_exports.string().max(MAX_LINE).optional().describe("\uB9C8\uAC10\uC77C YYYY-MM-DD"),
|
|
44072
|
+
source: external_exports.string().max(MAX_LINE).optional().describe("\uCD9C\uCC98 (\uC0AC\uB78C\uC778/\uC6D0\uD2F0\uB4DC/\uC9C1\uC811 \uC785\uB825 \uB4F1)")
|
|
44060
44073
|
});
|
|
44061
44074
|
var JobRecordSchema = external_exports.object({
|
|
44062
44075
|
...baseRecord,
|
|
@@ -44072,14 +44085,14 @@ var JobRecordSchema = external_exports.object({
|
|
|
44072
44085
|
source: external_exports.string().nullable()
|
|
44073
44086
|
});
|
|
44074
44087
|
var FitAnalysisInputSchema = external_exports.object({
|
|
44075
|
-
job_id:
|
|
44088
|
+
job_id: reqLine.describe("\uBD84\uC11D \uB300\uC0C1 \uACF5\uACE0 ID"),
|
|
44076
44089
|
score: external_exports.number().min(0).max(100).optional().describe("\uC885\uD569 \uC801\uD569\uB3C4 0~100"),
|
|
44077
|
-
summary: external_exports.string().optional().describe("\uD55C\uB450 \uBB38\uB2E8 \uC694\uC57D"),
|
|
44078
|
-
strengths:
|
|
44079
|
-
gaps:
|
|
44080
|
-
matched_keywords:
|
|
44081
|
-
missing_keywords:
|
|
44082
|
-
recommendations:
|
|
44090
|
+
summary: external_exports.string().max(MAX_NOTE).optional().describe("\uD55C\uB450 \uBB38\uB2E8 \uC694\uC57D"),
|
|
44091
|
+
strengths: strList.optional().describe("\uAC15\uC810 / \uC798 \uB9DE\uB294 \uBD80\uBD84"),
|
|
44092
|
+
gaps: strList.optional().describe("\uBD80\uC871\uD55C \uBD80\uBD84 / \uBCF4\uC644 \uD544\uC694"),
|
|
44093
|
+
matched_keywords: strList.optional(),
|
|
44094
|
+
missing_keywords: strList.optional(),
|
|
44095
|
+
recommendations: strList.optional().describe("\uC790\uAE30\uC18C\uAC1C\uC11C/\uC9C0\uC6D0 \uC804\uB7B5 \uC81C\uC548")
|
|
44083
44096
|
});
|
|
44084
44097
|
var FitAnalysisRecordSchema = external_exports.object({
|
|
44085
44098
|
...baseRecord,
|
|
@@ -44093,18 +44106,18 @@ var FitAnalysisRecordSchema = external_exports.object({
|
|
|
44093
44106
|
recommendations: external_exports.array(external_exports.string())
|
|
44094
44107
|
});
|
|
44095
44108
|
var ApplicationInputSchema = external_exports.object({
|
|
44096
|
-
job_id:
|
|
44109
|
+
job_id: reqLine,
|
|
44097
44110
|
status: external_exports.enum(APPLICATION_STATUSES).optional(),
|
|
44098
|
-
resume_id:
|
|
44099
|
-
cover_letter_id:
|
|
44100
|
-
applied_at:
|
|
44101
|
-
notes:
|
|
44111
|
+
resume_id: optLine,
|
|
44112
|
+
cover_letter_id: optLine,
|
|
44113
|
+
applied_at: optLine,
|
|
44114
|
+
notes: optNote
|
|
44102
44115
|
});
|
|
44103
44116
|
var ApplicationStatusUpdateSchema = external_exports.object({
|
|
44104
|
-
job_id:
|
|
44105
|
-
application_id:
|
|
44117
|
+
job_id: optLine,
|
|
44118
|
+
application_id: optLine,
|
|
44106
44119
|
status: external_exports.enum(APPLICATION_STATUSES),
|
|
44107
|
-
note:
|
|
44120
|
+
note: optNote.describe("\uC0C1\uD0DC \uBCC0\uACBD \uC0AC\uC720/\uBA54\uBAA8")
|
|
44108
44121
|
});
|
|
44109
44122
|
var ApplicationRecordSchema = external_exports.object({
|
|
44110
44123
|
...baseRecord,
|
|
@@ -44116,24 +44129,24 @@ var ApplicationRecordSchema = external_exports.object({
|
|
|
44116
44129
|
notes: external_exports.string().nullable()
|
|
44117
44130
|
});
|
|
44118
44131
|
var StarGuideSchema = external_exports.object({
|
|
44119
|
-
question: external_exports.string(),
|
|
44120
|
-
situation:
|
|
44121
|
-
task:
|
|
44122
|
-
action:
|
|
44123
|
-
result:
|
|
44132
|
+
question: external_exports.string().max(MAX_NOTE),
|
|
44133
|
+
situation: optNote,
|
|
44134
|
+
task: optNote,
|
|
44135
|
+
action: optNote,
|
|
44136
|
+
result: optNote
|
|
44124
44137
|
});
|
|
44125
44138
|
var InterviewQuestionSchema = external_exports.object({
|
|
44126
|
-
question: external_exports.string(),
|
|
44127
|
-
intent: external_exports.string().optional().describe("\uC9C8\uBB38 \uC758\uB3C4"),
|
|
44128
|
-
followups:
|
|
44129
|
-
answer_outline:
|
|
44139
|
+
question: external_exports.string().max(MAX_NOTE),
|
|
44140
|
+
intent: external_exports.string().max(MAX_NOTE).optional().describe("\uC9C8\uBB38 \uC758\uB3C4"),
|
|
44141
|
+
followups: strList.optional().describe("\uC608\uC0C1 \uAF2C\uB9AC \uC9C8\uBB38"),
|
|
44142
|
+
answer_outline: optBody.describe("\uB2F5\uBCC0 \uAC00\uC774\uB4DC/\uD575\uC2EC \uD3EC\uC778\uD2B8")
|
|
44130
44143
|
});
|
|
44131
44144
|
var InterviewPrepInputSchema = external_exports.object({
|
|
44132
|
-
job_id:
|
|
44133
|
-
questions: external_exports.array(InterviewQuestionSchema).optional(),
|
|
44134
|
-
star_guides: external_exports.array(StarGuideSchema).optional(),
|
|
44135
|
-
self_introduction:
|
|
44136
|
-
notes:
|
|
44145
|
+
job_id: reqLine,
|
|
44146
|
+
questions: external_exports.array(InterviewQuestionSchema).max(MAX_ITEMS).optional(),
|
|
44147
|
+
star_guides: external_exports.array(StarGuideSchema).max(MAX_ITEMS).optional(),
|
|
44148
|
+
self_introduction: optBody.describe("1\uBD84 \uC790\uAE30\uC18C\uAC1C \uCD08\uC548"),
|
|
44149
|
+
notes: optNote.describe("\uBA74\uC811 \uD6C4\uAE30/\uBA54\uBAA8")
|
|
44137
44150
|
});
|
|
44138
44151
|
var InterviewPrepRecordSchema = external_exports.object({
|
|
44139
44152
|
...baseRecord,
|
|
@@ -44187,7 +44200,7 @@ var ApplicationContextSchema = external_exports.object({
|
|
|
44187
44200
|
});
|
|
44188
44201
|
|
|
44189
44202
|
// packages/shared/src/build-info.ts
|
|
44190
|
-
var APP_VERSION = true ? "0.1.
|
|
44203
|
+
var APP_VERSION = true ? "0.1.4" : "0.0.0-dev";
|
|
44191
44204
|
var BUNDLED = true;
|
|
44192
44205
|
|
|
44193
44206
|
// packages/shared/src/index.ts
|
|
@@ -44903,7 +44916,21 @@ var activityRepo = {
|
|
|
44903
44916
|
};
|
|
44904
44917
|
function getEntityCounts() {
|
|
44905
44918
|
const db = getDb();
|
|
44906
|
-
const
|
|
44919
|
+
const ALLOWED_TABLES = /* @__PURE__ */ new Set([
|
|
44920
|
+
"profile",
|
|
44921
|
+
"experiences",
|
|
44922
|
+
"projects",
|
|
44923
|
+
"skills",
|
|
44924
|
+
"documents",
|
|
44925
|
+
"cover_letters",
|
|
44926
|
+
"jobs",
|
|
44927
|
+
"applications",
|
|
44928
|
+
"interview_preps"
|
|
44929
|
+
]);
|
|
44930
|
+
const c = (t) => {
|
|
44931
|
+
if (!ALLOWED_TABLES.has(t)) throw new Error(`getEntityCounts: \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uC740 \uD14C\uC774\uBE14 \uC774\uB984: ${t}`);
|
|
44932
|
+
return db.prepare(`SELECT COUNT(*) AS n FROM ${t}`).get().n;
|
|
44933
|
+
};
|
|
44907
44934
|
return {
|
|
44908
44935
|
profile: c("profile"),
|
|
44909
44936
|
experiences: c("experiences"),
|
|
@@ -44979,7 +45006,7 @@ function injectToken(html) {
|
|
|
44979
45006
|
// apps/web/src/http.ts
|
|
44980
45007
|
import fs3 from "node:fs";
|
|
44981
45008
|
import path3 from "node:path";
|
|
44982
|
-
var
|
|
45009
|
+
var MAX_BODY2 = 8 * 1024 * 1024;
|
|
44983
45010
|
var Router = class {
|
|
44984
45011
|
routes = [];
|
|
44985
45012
|
add(method, path6, handler) {
|
|
@@ -45074,7 +45101,7 @@ function readBody(req) {
|
|
|
45074
45101
|
const chunks = [];
|
|
45075
45102
|
req.on("data", (c) => {
|
|
45076
45103
|
size2 += c.length;
|
|
45077
|
-
if (size2 >
|
|
45104
|
+
if (size2 > MAX_BODY2) {
|
|
45078
45105
|
reject2(new HttpError(413, "\uC694\uCCAD \uBCF8\uBB38\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4.", "too_large"));
|
|
45079
45106
|
req.destroy();
|
|
45080
45107
|
return;
|
package/docs/FAQ.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# 자주 묻는 질문 / 문제 해결 (FAQ & Troubleshooting)
|
|
2
|
+
|
|
3
|
+
설치·연결·사용 중 자주 막히는 지점을 모았습니다. 설치 자체가 처음이라면
|
|
4
|
+
[`../INSTALL.md`](../INSTALL.md)를, 연결 직후 무엇을 할지는
|
|
5
|
+
[`START_WORKFLOW.md`](START_WORKFLOW.md)를 먼저 보세요.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 설치 / 연결
|
|
10
|
+
|
|
11
|
+
### "MCP 도구가 안 보여요" / AI가 CareerMate를 모릅니다
|
|
12
|
+
1. AI 클라이언트를 **완전히 종료한 뒤 다시 시작**했는지 확인하세요(설정만 바꾸고 재시작을
|
|
13
|
+
안 하면 적용되지 않습니다).
|
|
14
|
+
2. Claude Code는 첫 실행 시 프로젝트 서버에 대한 **1회 승인**을 묻습니다 — 승인하세요.
|
|
15
|
+
3. 클라이언트의 `/mcp` 명령으로 `careermate`가 연결됐는지 확인하거나, AI에게
|
|
16
|
+
"온보딩 상태 확인해줘"라고 해 응답이 오는지 보세요.
|
|
17
|
+
4. 그래도 안 되면 클라이언트별 등록 방법을 [`../INSTALL.md`](../INSTALL.md)와
|
|
18
|
+
[`SUPPORTED_AI_APPS.md`](SUPPORTED_AI_APPS.md)에서 확인하세요.
|
|
19
|
+
|
|
20
|
+
### ChatGPT 웹/모바일, Gemini 웹에서 안 돼요
|
|
21
|
+
정상입니다. 이들은 클라우드에서 실행되어 **원격 URL MCP만** 쓸 수 있고, CareerMate는
|
|
22
|
+
사용자 컴퓨터에서 도는 **로컬 stdio 서버**라 닿지 못합니다. 로컬에서 실행되는
|
|
23
|
+
**Claude Desktop · Claude Code · Codex CLI** 를 사용하세요(자세한 이유는
|
|
24
|
+
[`SUPPORTED_AI_APPS.md`](SUPPORTED_AI_APPS.md)).
|
|
25
|
+
|
|
26
|
+
### Node 버전 오류가 나요
|
|
27
|
+
CareerMate는 **Node.js 22.5.0 이상**이 필요합니다(`node:sqlite` 사용).
|
|
28
|
+
`node -v`로 확인하고 필요하면 업데이트하세요.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 대시보드
|
|
33
|
+
|
|
34
|
+
### "대시보드가 안 열려요" (`http://127.0.0.1:4319`)
|
|
35
|
+
- 그 주소는 **CareerMate가 실행 중일 때만** 열립니다. AI에게 무언가를 저장해 달라고 해
|
|
36
|
+
서버가 떠 있는 상태인지 확인하세요.
|
|
37
|
+
- **포트가 이미 사용 중**이면 자동으로 다른 포트로 폴백합니다. 환경변수
|
|
38
|
+
`CAREERMATE_PORT`로 고정할 수 있습니다.
|
|
39
|
+
- 대시보드는 보안상 **`127.0.0.1`(이 컴퓨터)** 에만 열립니다. 다른 기기에서는 접속되지
|
|
40
|
+
않습니다(의도된 동작).
|
|
41
|
+
|
|
42
|
+
### 브라우저를 자동으로 안 열어요
|
|
43
|
+
첫 온보딩 때 한 번만 자동으로 엽니다. 이후에는 링크만 안내합니다 — 직접 주소를 열거나,
|
|
44
|
+
AI에게 "대시보드 열어줘"라고 하세요. 자동 열기를 끄려면 `CAREERMATE_NO_OPEN=1`.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 문서(이력서·공고) 읽기
|
|
49
|
+
|
|
50
|
+
### "PDF가 안 읽혀요"
|
|
51
|
+
- **스캔본/이미지 PDF**(텍스트 레이어 없음)는 추출되지 않습니다. 이미지를 직접 볼 수 있는
|
|
52
|
+
AI 클라이언트라면 PDF를 그대로 보여주고, 아니면 내용을 **텍스트로 붙여넣어** 주세요.
|
|
53
|
+
- **암호로 보호된 PDF**는 읽을 수 없습니다. 뷰어에서 암호를 해제해 다시 저장하거나 텍스트로
|
|
54
|
+
붙여넣으세요.
|
|
55
|
+
- 손상된 PDF, 25MB가 넘는 파일도 거부됩니다 — 안내 메시지에 이유가 나옵니다.
|
|
56
|
+
|
|
57
|
+
### 지원하는 파일 형식은?
|
|
58
|
+
PDF, Word(`.docx`), 한컴(`.hwp`/`.hwpx`), 텍스트(`.txt`/`.md`). 가능하면 **절대 경로**로
|
|
59
|
+
파일 위치를 알려 주세요(예: `C:\Users\me\Desktop\이력서.pdf`).
|
|
60
|
+
|
|
61
|
+
### 한글이 깨져요
|
|
62
|
+
CareerMate는 UTF-8과 한국어 인코딩(CP949/EUC-KR)을 자동 감지합니다. 그래도 깨지면 파일을
|
|
63
|
+
UTF-8로 다시 저장하거나 내용을 붙여넣어 주세요.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## 공고 분석 / 자소서
|
|
68
|
+
|
|
69
|
+
### "공고 URL은 어떻게 넣어요?"
|
|
70
|
+
URL을 그대로 붙여넣어도 되고, 공고 **본문 텍스트**를 붙여넣어도 됩니다. CareerMate는 LLM이
|
|
71
|
+
없어 URL을 직접 크롤링하지 않으므로, **공고 본문 텍스트가 있으면 분석 품질이 가장 좋습니다.**
|
|
72
|
+
AI에게 "이 공고 저장하고 분석해줘"라고 하면서 URL이나 본문을 함께 주세요.
|
|
73
|
+
|
|
74
|
+
### "분석이 안 돼요" / 결과가 비어요
|
|
75
|
+
- 분석은 **AI가** 수행하고 CareerMate는 입력(프로필·공고)과 결과를 저장합니다. 먼저
|
|
76
|
+
**프로필과 공고가 저장돼 있어야** 합니다.
|
|
77
|
+
- AI에게 "분석 전에 application context부터 불러와줘"라고 하면 프로필·이력서·공고를 한 번에
|
|
78
|
+
모아 분석 맥락을 갖춥니다.
|
|
79
|
+
|
|
80
|
+
### 면접 준비가 잠겨 있어요
|
|
81
|
+
면접 준비는 지원 상태가 **'서류 합격(document_passed)' 이상**일 때만 열립니다. 먼저 해당
|
|
82
|
+
공고의 상태를 서류 합격으로 바꿔 주세요(`draft → planned → applied → document_passed → …`).
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 데이터 / 프라이버시
|
|
87
|
+
|
|
88
|
+
### 내 데이터는 어디에 저장되나요?
|
|
89
|
+
사용자 컴퓨터의 `~/.careermate`(Windows: `%USERPROFILE%\.careermate`)에만 저장됩니다.
|
|
90
|
+
대시보드 **설정 > 데이터 저장 위치**에서 정확한 경로를 볼 수 있습니다. 외부로 전송되지
|
|
91
|
+
않습니다.
|
|
92
|
+
|
|
93
|
+
### 백업하거나 전부 삭제하려면?
|
|
94
|
+
대시보드 **설정 > 백업 / 내보내기**에서 백업 생성·전체 JSON 내보내기를, **위험 구역**에서
|
|
95
|
+
전체 초기화를 할 수 있습니다(초기화 직전 자동 백업).
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
여기서 해결되지 않으면 [GitHub 이슈](https://github.com/osntak/CareerMate/issues)에 남겨
|
|
100
|
+
주세요. **보안 문제**는 이슈가 아니라 [`../SECURITY.md`](../SECURITY.md)의 비공개 절차를
|
|
101
|
+
이용해 주세요.
|