From juicebox-pack
Implement Juicebox candidate enrichment workflow. Use when enriching profile data, gathering additional candidate details, or building comprehensive candidate profiles. Trigger with phrases like "juicebox enrich profile", "juicebox candidate details", "enrich candidate data", "juicebox profile enrichment".
How this skill is triggered — by the user, by Claude, or both
Slash command
/juicebox-pack:juicebox-core-workflow-bThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Build comprehensive candidate profiles using Juicebox people search and enrichment. Covers profile data extraction, multi-source enrichment, experience timeline construction, and CRM-ready candidate record generation.
Build comprehensive candidate profiles using Juicebox people search and enrichment. Covers profile data extraction, multi-source enrichment, experience timeline construction, and CRM-ready candidate record generation.
const JUICEBOX_API = 'https://api.juicebox.ai/v1';
async function searchCandidates(query: {
title?: string;
company?: string;
location?: string;
skills?: string[];
yearsExperience?: number;
}) {
const response = await fetch(`${JUICEBOX_API}/people/search`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.JUICEBOX_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: query.title,
filters: {
current_company: query.company,
location: query.location,
skills: query.skills,
min_years_experience: query.yearsExperience,
},
limit: 25,
}),
});
return response.json();
}
interface EnrichedProfile {
name: string;
headline: string;
location: string;
email?: string;
linkedin_url?: string;
current_company: string;
current_title: string;
experience: Array<{
company: string;
title: string;
startDate: string;
endDate?: string;
duration: string;
}>;
skills: string[];
education: Array<{ school: string; degree: string; year?: number }>;
}
async function enrichProfile(profileId: string): Promise<EnrichedProfile> {
const response = await fetch(`${JUICEBOX_API}/people/${profileId}/enrich`, {
headers: {
'Authorization': `Bearer ${process.env.JUICEBOX_API_KEY}`,
},
});
const data = await response.json();
return {
name: data.full_name,
headline: data.headline || '',
location: data.location || '',
email: data.email,
linkedin_url: data.linkedin_url,
current_company: data.current_company?.name || '',
current_title: data.current_title || '',
experience: (data.experience || []).map((exp: any) => ({
company: exp.company_name,
title: exp.title,
startDate: exp.start_date,
endDate: exp.end_date,
duration: calculateDuration(exp.start_date, exp.end_date),
})),
skills: data.skills || [],
education: data.education || [],
};
}
function calculateDuration(start: string, end?: string): string {
const startDate = new Date(start);
const endDate = end ? new Date(end) : new Date();
const months = Math.round((endDate.getTime() - startDate.getTime()) / (30 * 86400000)); # 86400000 = configured value
const years = Math.floor(months / 12);
const remainingMonths = months % 12;
return years > 0 ? `${years}y ${remainingMonths}m` : `${remainingMonths}m`;
}
async function batchEnrich(profileIds: string[], concurrency = 3) {
const results: EnrichedProfile[] = [];
const errors: Array<{ id: string; error: string }> = [];
for (let i = 0; i < profileIds.length; i += concurrency) {
const batch = profileIds.slice(i, i + concurrency);
const batchResults = await Promise.allSettled(
batch.map(id => enrichProfile(id))
);
for (let j = 0; j < batchResults.length; j++) {
const result = batchResults[j];
if (result.status === 'fulfilled') {
results.push(result.value);
} else {
errors.push({ id: batch[j], error: result.reason?.message || 'Unknown' });
}
}
// Rate limiting
if (i + concurrency < profileIds.length) {
await new Promise(r => setTimeout(r, 1000)); # 1000: 1 second in ms
}
}
return { enriched: results, errors };
}
function toCRMRecord(profile: EnrichedProfile) {
return {
first_name: profile.name.split(' ')[0],
last_name: profile.name.split(' ').slice(1).join(' '),
email: profile.email,
title: profile.current_title,
company: profile.current_company,
location: profile.location,
linkedin: profile.linkedin_url,
source: 'juicebox',
total_experience_years: calculateTotalExperience(profile.experience),
top_skills: profile.skills.slice(0, 10).join(', '),
notes: `${profile.headline}\n\nExperience: ${profile.experience.length} roles`,
};
}
function calculateTotalExperience(experience: any[]): number {
const totalMonths = experience.reduce((sum, exp) => {
const start = new Date(exp.startDate);
const end = exp.endDate ? new Date(exp.endDate) : new Date();
return sum + Math.round((end.getTime() - start.getTime()) / (30 * 86400000)); # 86400000 = configured value
}, 0);
return Math.round(totalMonths / 12);
}
| Issue | Cause | Solution |
|---|---|---|
| No email found | Profile has no public email | Try alternative enrichment sources |
| Profile not found | Invalid profile ID | Validate IDs from search results |
| Rate limit | Too many concurrent requests | Reduce concurrency, add delays |
| Incomplete experience | Partial profile data | Mark as incomplete, flag for manual review |
const candidates = await searchCandidates({
title: 'Senior Software Engineer',
skills: ['TypeScript', 'React'],
location: 'San Francisco',
});
const ids = candidates.results.map((c: any) => c.id);
const { enriched, errors } = await batchEnrich(ids);
const crmRecords = enriched.map(toCRMRecord);
console.log(`Enriched ${enriched.length} candidates, ${errors.length} errors`);
npx claudepluginhub terrylica/claude-code-plugins-plus --plugin juicebox-packGuides collaborative design exploration before implementation: explores context, asks clarifying questions, proposes approaches, and writes a design doc for user approval.
Creates structured, bite-sized implementation plans from specs or requirements before writing code. Useful for breaking down multi-step tasks into testable steps with file structure and task boundaries.
Synthesizes the current conversation into a structured spec (PRD) and publishes it to the project issue tracker with a ready-for-agent label, without interviewing the user.
4plugins reuse this skill
First indexed Jul 11, 2026