Skip to content

Commit e4e5dc8

Browse files
authored
feat: robust skills management (#610) (#620)
* feat: skills management production-grade (#610) - Added profile.skill_remove action - Extracted skills list in profile read - Handled empty skills dropdown cases - Added MCP tools for read/remove skills - Improved reorder robustness * fix #610: resolve typescript errors in profile seed and mcp tools
1 parent 6efafaf commit e4e5dc8

File tree

8 files changed

+479
-21
lines changed

8 files changed

+479
-21
lines changed

packages/cli/src/profileSeed.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ const SECTION_KEY_ALIASES = new Map<string, LinkedInProfileSectionType>([
2727
["volunteerexperience", "volunteer_experience"],
2828
["volunteer_experience", "volunteer_experience"],
2929
["honorsawards", "honors_awards"],
30-
["honors_awards", "honors_awards"]
30+
["honors_awards", "honors_awards"],
31+
["skills", "skills"]
3132
]);
3233

3334
const INTRO_FIELD_KEYS = new Set(["firstName", "lastName", "headline", "location"]);
@@ -45,7 +46,8 @@ const SECTION_IDENTITY_FIELDS: Record<Exclude<LinkedInProfileSectionType, "about
4546
languages: ["name"],
4647
projects: ["title"],
4748
volunteer_experience: ["role", "organization"],
48-
honors_awards: ["title", "issuer"]
49+
honors_awards: ["title", "issuer"],
50+
skills: ["name"]
4951
};
5052

5153
type SeedSectionType = Exclude<LinkedInProfileSectionType, "about">;
@@ -119,12 +121,12 @@ export function parseProfileSeedSpec(input: unknown): ProfileSeedSpec {
119121
continue;
120122
}
121123

122-
if (rawKey === "skills") {
124+
if (rawKey === "recommendations") {
123125
if (Array.isArray(rawValue) && rawValue.length > 0) {
124126
unsupportedFields.push({
125-
path: "skills",
126-
reason: "Skills are not exposed by the current LinkedIn profile edit automation.",
127-
issueNumber: 228
127+
path: "recommendations",
128+
reason: "Recommendations are not exposed by the current LinkedIn profile edit automation.",
129+
issueNumber: 0
128130
});
129131
}
130132
continue;
@@ -636,6 +638,8 @@ function describeSectionValues(
636638
return joinSummaryParts(values.role, values.organization);
637639
case "honors_awards":
638640
return joinSummaryParts(values.title, values.issuer);
641+
case "skills":
642+
return readString(values.name);
639643
}
640644
}
641645

@@ -653,6 +657,8 @@ function describeSectionItem(
653657
return joinSummaryParts(item.primary_text, item.secondary_text);
654658
case "projects":
655659
return readString(item.primary_text);
660+
case "skills":
661+
return readString(item.primary_text);
656662
}
657663
}
658664

packages/cli/test/profileCli.test.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ describe("CLI profile commands", () => {
319319
customProfileUrl: "avery-automation"
320320
},
321321
about: "Building production LLM systems.",
322-
skills: ["TypeScript", "Python"]
322+
recommendations: ["TypeScript", "Python"]
323323
},
324324
null,
325325
2
@@ -358,9 +358,9 @@ describe("CLI profile commands", () => {
358358
]);
359359
expect(output.unsupported_fields).toEqual([
360360
{
361-
path: "skills",
362-
reason: "Skills are not exposed by the current LinkedIn profile edit automation.",
363-
issueNumber: 228
361+
path: "recommendations",
362+
reason: "Recommendations are not exposed by the current LinkedIn profile edit automation.",
363+
issueNumber: 0
364364
}
365365
]);
366366
expect(profileCliMocks.prepareUpdateIntro).toHaveBeenCalledWith(
@@ -401,7 +401,7 @@ describe("CLI profile commands", () => {
401401
intro: {
402402
headline: "Automation Engineer at Example Labs"
403403
},
404-
skills: ["TypeScript"]
404+
recommendations: ["TypeScript"]
405405
},
406406
null,
407407
2
@@ -420,7 +420,7 @@ describe("CLI profile commands", () => {
420420
specPath,
421421
"--yes"
422422
])
423-
).rejects.toThrow("Profile seed spec includes unsupported fields: skills (#228)");
423+
).rejects.toThrow("Profile seed spec includes unsupported fields: recommendations (#0)");
424424

425425
expect(profileCliMocks.prepareUpdateIntro).not.toHaveBeenCalled();
426426
expect(profileCliMocks.confirmByToken).not.toHaveBeenCalled();

packages/cli/test/profileSeed.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ describe("profile seed planner", () => {
7676
issueYear: "2024",
7777
},
7878
],
79-
skills: ["TypeScript", "Python"],
79+
recommendations: ["TypeScript", "Python"],
8080
});
8181

8282
expect(spec.intro).toMatchObject({
@@ -89,10 +89,10 @@ describe("profile seed planner", () => {
8989
expect(spec.sections.certifications).toHaveLength(1);
9090
expect(spec.unsupportedFields).toEqual([
9191
{
92-
path: "skills",
92+
path: "recommendations",
9393
reason:
94-
"Skills are not exposed by the current LinkedIn profile edit automation.",
95-
issueNumber: 228,
94+
"Recommendations are not exposed by the current LinkedIn profile edit automation.",
95+
issueNumber: 0,
9696
},
9797
]);
9898
});

packages/core/src/__tests__/e2e/helpers.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ import {
108108
LINKEDIN_PROFILE_PREPARE_FEATURED_REORDER_TOOL,
109109
LINKEDIN_PROFILE_PREPARE_ADD_SKILL_TOOL,
110110
LINKEDIN_PROFILE_PREPARE_REORDER_SKILLS_TOOL,
111+
LINKEDIN_PROFILE_READ_SKILLS_TOOL,
112+
LINKEDIN_PROFILE_PREPARE_REMOVE_SKILL_TOOL,
111113
LINKEDIN_PROFILE_PREPARE_ENDORSE_SKILL_TOOL,
112114
LINKEDIN_PROFILE_PREPARE_REQUEST_RECOMMENDATION_TOOL,
113115
LINKEDIN_PROFILE_PREPARE_WRITE_RECOMMENDATION_TOOL,
@@ -1060,6 +1062,8 @@ export const MCP_TOOL_NAMES = {
10601062
profilePrepareFeaturedReorder: LINKEDIN_PROFILE_PREPARE_FEATURED_REORDER_TOOL,
10611063
profilePrepareAddSkill: LINKEDIN_PROFILE_PREPARE_ADD_SKILL_TOOL,
10621064
profilePrepareReorderSkills: LINKEDIN_PROFILE_PREPARE_REORDER_SKILLS_TOOL,
1065+
profileReadSkills: LINKEDIN_PROFILE_READ_SKILLS_TOOL,
1066+
profilePrepareRemoveSkill: LINKEDIN_PROFILE_PREPARE_REMOVE_SKILL_TOOL,
10631067
profilePrepareEndorseSkill: LINKEDIN_PROFILE_PREPARE_ENDORSE_SKILL_TOOL,
10641068
profilePrepareRequestRecommendation:
10651069
LINKEDIN_PROFILE_PREPARE_REQUEST_RECOMMENDATION_TOOL,

packages/core/src/__tests__/linkedinProfile.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,7 @@ describe("profile action type constants", () => {
10391039
"projects",
10401040
"volunteer_experience",
10411041
"honors_awards",
1042+
"skills"
10421043
]);
10431044
expect(LINKEDIN_PROFILE_SECTION_TYPES).not.toContain("featured");
10441045
expect(LINKEDIN_PROFILE_FEATURED_ITEM_KINDS).toEqual([

0 commit comments

Comments
 (0)