careermate 0.0.3 → 0.0.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/dist/doctor.mjs +185 -39
- package/dist/mcp.mjs +188 -42
- package/dist/migrate.mjs +23 -1
- package/dist/public/lib.js +5 -1
- package/dist/public/pages/applications.js +7 -0
- package/dist/public/pages/jobs.js +22 -15
- package/dist/public/styles.css +9 -0
- package/dist/site/index.html +3 -43
- package/dist/site/start.html +122 -102
- package/dist/web.mjs +138 -13
- package/docs/MCP_TOOLS.md +6 -5
- package/docs/START_WORKFLOW.md +2 -1
- package/docs/WORKFLOWS.md +1 -1
- package/package.json +1 -1
- package/packages/core/src/services.ts +67 -5
- package/packages/db/src/repositories.ts +0 -0
- package/packages/mcp-tools/src/tools.ts +21 -18
- package/packages/prompts/src/onboarding.ts +1 -1
- package/packages/shared/src/enums.ts +7 -2
- package/packages/shared/src/errors.ts +33 -0
- package/packages/shared/src/index.ts +1 -0
- package/packages/shared/src/schemas.ts +56 -2
- package/packages/workflows/src/definitions.ts +1 -1
|
@@ -72,8 +72,32 @@ const reqBody = z
|
|
|
72
72
|
.string()
|
|
73
73
|
.max(MAX_BODY)
|
|
74
74
|
.refine((s) => s.trim().length > 0, { message: EMPTY_MSG });
|
|
75
|
-
/**
|
|
76
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Coerce a stray scalar into a string array. MCP/LLM clients routinely serialize
|
|
77
|
+
* array arguments as a *string* — a JSON-encoded array (`'["Java","Spring"]'`) or
|
|
78
|
+
* a comma/newline-separated list (`'Java, Spring'`) — instead of a real array. The
|
|
79
|
+
* SDK then rejects the whole tool call with "Expected array, received string", so
|
|
80
|
+
* a single mis-serialized field (keywords, requirements, strengths, tech, tags…)
|
|
81
|
+
* fails the entire save. Absorb those shapes here; real arrays and non-strings
|
|
82
|
+
* pass straight through. The emitted JSON schema still advertises `array`, so the
|
|
83
|
+
* client is still told the correct shape — this only rescues the cases it ignores.
|
|
84
|
+
*/
|
|
85
|
+
function toStrList(v: unknown): unknown {
|
|
86
|
+
if (typeof v !== 'string') return v;
|
|
87
|
+
const s = v.trim();
|
|
88
|
+
if (!s) return [];
|
|
89
|
+
if (s.startsWith('[')) {
|
|
90
|
+
try {
|
|
91
|
+
const parsed = JSON.parse(s);
|
|
92
|
+
if (Array.isArray(parsed)) return parsed;
|
|
93
|
+
} catch {
|
|
94
|
+
/* not valid JSON — fall through to delimiter split */
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return s.split(/[\n,]/).map((x) => x.trim()).filter(Boolean);
|
|
98
|
+
}
|
|
99
|
+
/** Bounded array of bounded strings; tolerant of stringified inputs (see toStrList). */
|
|
100
|
+
const strList = z.preprocess(toStrList, z.array(z.string().max(MAX_ITEM)).max(MAX_ITEMS));
|
|
77
101
|
|
|
78
102
|
const baseRecord = {
|
|
79
103
|
id,
|
|
@@ -151,6 +175,16 @@ export const ExperienceRecordSchema = z.object({
|
|
|
151
175
|
});
|
|
152
176
|
export type ExperienceRecord = z.infer<typeof ExperienceRecordSchema>;
|
|
153
177
|
|
|
178
|
+
/** Batch input: add several experiences in one MCP call (one item → array of one). */
|
|
179
|
+
export const ExperienceBatchInputSchema = z.object({
|
|
180
|
+
experiences: z
|
|
181
|
+
.array(ExperienceInputSchema)
|
|
182
|
+
.min(1)
|
|
183
|
+
.max(MAX_ITEMS)
|
|
184
|
+
.describe('추가할 경력 목록(한 건이어도 배열로). 같은 회사·직무·입사일은 갱신되고 중복 생성되지 않습니다.'),
|
|
185
|
+
});
|
|
186
|
+
export type ExperienceBatchInput = z.infer<typeof ExperienceBatchInputSchema>;
|
|
187
|
+
|
|
154
188
|
/* ------------------------------------------------------------------ Project */
|
|
155
189
|
|
|
156
190
|
export const ProjectInputSchema = z.object({
|
|
@@ -180,6 +214,16 @@ export const ProjectRecordSchema = z.object({
|
|
|
180
214
|
});
|
|
181
215
|
export type ProjectRecord = z.infer<typeof ProjectRecordSchema>;
|
|
182
216
|
|
|
217
|
+
/** Batch input: add several projects in one MCP call (one item → array of one). */
|
|
218
|
+
export const ProjectBatchInputSchema = z.object({
|
|
219
|
+
projects: z
|
|
220
|
+
.array(ProjectInputSchema)
|
|
221
|
+
.min(1)
|
|
222
|
+
.max(MAX_ITEMS)
|
|
223
|
+
.describe('추가할 프로젝트 목록(한 개여도 배열로). 같은 이름의 프로젝트는 갱신되고 중복 생성되지 않습니다.'),
|
|
224
|
+
});
|
|
225
|
+
export type ProjectBatchInput = z.infer<typeof ProjectBatchInputSchema>;
|
|
226
|
+
|
|
183
227
|
/* -------------------------------------------------------------------- Skill */
|
|
184
228
|
|
|
185
229
|
export const SkillInputSchema = z.object({
|
|
@@ -201,6 +245,16 @@ export const SkillRecordSchema = z.object({
|
|
|
201
245
|
});
|
|
202
246
|
export type SkillRecord = z.infer<typeof SkillRecordSchema>;
|
|
203
247
|
|
|
248
|
+
/** Batch input: add several skills in one MCP call (one item → array of one). */
|
|
249
|
+
export const SkillBatchInputSchema = z.object({
|
|
250
|
+
skills: z
|
|
251
|
+
.array(SkillInputSchema)
|
|
252
|
+
.min(1)
|
|
253
|
+
.max(MAX_ITEMS)
|
|
254
|
+
.describe('추가할 기술스택 목록(한 개여도 배열로). 같은 이름의 기술은 갱신되고 중복 생성되지 않습니다.'),
|
|
255
|
+
});
|
|
256
|
+
export type SkillBatchInput = z.infer<typeof SkillBatchInputSchema>;
|
|
257
|
+
|
|
204
258
|
/* ----------------------------------------------------------------- Document */
|
|
205
259
|
|
|
206
260
|
export const DocumentInputSchema = z.object({
|
|
@@ -33,7 +33,7 @@ export const WORKFLOWS: WorkflowDefinition[] = [
|
|
|
33
33
|
'파일 입력 분기: ① 사용자가 "파일이 있다"고 하면 `open_inbox`로 인입 폴더를 열어 파일을 넣게 하고, "다 넣었다"고 하면 `read_inbox`로 본문을 읽는다(pdf·이미지는 read_inbox가 경로만 주므로 클라이언트의 파일 읽기로 직접 읽는다). ② 사용자가 파일 경로를 직접 알려주면 `read_document`로 읽는다. ③ "없다/직접 입력/텍스트로 붙여넣기"면 폴더를 열지 말고 받은 텍스트를 그대로 쓴다. (묻지도 않고 open_inbox로 폴더를 자동으로 열지 않는다.)',
|
|
34
34
|
'수집한 정보를 구조화해 `save_profile`로 저장한다(이름·연락처·headline·summary·desired_roles·desired_conditions, 그리고 글쓰기 선호인 preferred_tone과 emphasis_points 포함). 파일 출처는 source=upload, 직접 입력은 manual.',
|
|
35
35
|
'업로드한 이력서/경력기술서/포트폴리오 본문을 `add_resume`로 저장한다(kind·title 지정, 대표 문서는 is_primary=true).',
|
|
36
|
-
'경력·프로젝트·스킬이 파악되면 `add_experience`·`add_project`·`add_skill`로
|
|
36
|
+
'경력·프로젝트·스킬이 파악되면 `add_experience`·`add_project`·`add_skill`로 저장한다 — 각 도구는 배열을 받으므로 파악한 항목을 한 번에 모아 넘긴다(항목마다 반복 호출하지 않는다). 같은 항목을 다시 넣어도 중복 없이 갱신된다(완성도의 has_experience/has_skills에 반영).',
|
|
37
37
|
'기존 자기소개서가 있으면 `save_cover_letter_version`으로 저장한다(title+content면 v1로 저장, 없으면 나중에 작성 가능함을 안내).',
|
|
38
38
|
'`open_dashboard`를 호출해 사용자가 자기 데이터를 확인하게 한다.',
|
|
39
39
|
'다시 `get_onboarding_status`로 완성도를 확인하고, 다음 단계(공고 분석 등)를 제안한다.',
|