Skill: agistack/recruiting (version 2.3.0)

--- name: recruiting-pro description: Hiring workflow management with structured processes and candidate tracking. Use when user mentions hiring, job descriptions, resume screening, interviews, candidate pipeline, or offer letters. Creates job descriptions, screens candidates, prepares interview questions, tracks pipeline stages, and drafts communications. All candidate data stored locally. NEVER makes hiring decisions or replaces human judgment. --- # Recruiting Structured hiring system. Better process, better hires. ## Critical Privacy & Safety ### Data Storage (CRITICAL) - **All hiring data stored locally only**: `memory/recruiting/` - **Candidate information never shared** externally - **No integration** with external ATS or HR systems - **No resume parsing services** - manual review only - User controls all data retention and deletion ### Safety Boundaries (NON-NEGOTIABLE) - ✅ Write job descriptions and screening criteria - ✅ Generate structured interview questions - ✅ Track candidate pipeline stages - ✅ Draft communications (for human review) - ❌ **NEVER make hiring decisions** - ❌ **NEVER store sensitive personal data** (SSN, DOB, etc.) - ❌ **NEVER guarantee candidate quality** or predict success - ❌ **NEVER replace** human judgment in hiring ### Fair Hiring Note Structured processes improve fairness but do not eliminate bias. Human oversight required at all decision points. ## Quick Start ### Data Storage Setup Hiring data stored in your local workspace: - `memory/recruiting/jobs.json` - Open positions and requirements - `memory/recruiting/candidates.json` - Candidate profiles and status - `memory/recruiting/pipeline.json` - Hiring pipeline stages - `memory/recruiting/interviews.json` - Interview guides and notes - `memory/recruiting/communications.json` - Email templates and drafts Use provided scripts in `scripts/` for all data operations. ## Core Workflows ### Create Job Description ``` User: "Write a job description for a senior engineer" → Use scripts/create_job.py --title "Senior Engineer" --level senior → Generate JD with requirements, responsibilities, and screening criteria ``` ### Screen Candidate ``` User: "Screen this resume for the PM role" → Use scripts/screen_candidate.py --job-id "JOB-123" --resume "resume.pdf" → Evaluate against job criteria, output match assessment ``` ### Prepare Interview ``` User: "Prepare interview questions for the design role" → Use scripts/prep_interview.py --job-id "JOB-123" --type behavioral → Generate structured question set ``` ### Track Pipeline ``` User: "Update candidate status to phone screen complete" → Use scripts/update_pipeline.py --candidate-id "CAND-456" --stage "phone-screen" --status completed → Move candidate in pipeline, set next actions ``` ### Draft Communication ``` User: "Draft rejection email for candidate" → Use scripts/draft_email.py --type rejection --candidate-id "CAND-456" → Generate professional, personalized message for human review ``` ## Module Reference For detailed implementation: - **Job Descriptions**: See [references/job-descriptions.md](references/job-descriptions.md) - **Resume Screening**: See [references/resume-screening.md](references/resume-screening.md) - **Interview Preparation**: See [references/interview-prep.md](references/interview-prep.md) - **Pipeline Tracking**: See [references/pipeline-tracking.md](references/pipeline-tracking.md) - **Communications**: See [references/communications.md](references/communications.md) - **Fair Hiring**: See [references/fair-hiring.md](references/fair-hiring.md) ## Scripts Reference | Script | Purpose | |--------|---------| | `create_job.py` | Create job posting with requirements | | `screen_candidate.py` | Evaluate resume against criteria | | `prep_interview.py` | Generate interview question sets | | `add_candidate.py` | Add candidate to pipeline | | `update_pipeline.py` | Move candidate through stages | | `view_pipeline.py` | Show current pipeline status | | `draft_email.py` | Generate communications | | `set_reminder.py` | Set follow-up reminders | | `generate_report.py` | Create hiring metrics report | ## Disclaimer This skill provides hiring process support only. All hiring decisions remain the responsibility of the hiring manager and organization. The skill does not guarantee candidate quality or success. Always comply with applicable employment laws and regulations.

path: references/job-descriptions.md
content: # Job Description Creation

## Purpose
Create job descriptions that function as filters - attracting the right candidates and repelling the wrong ones.

## When to Use
- "Write a job description for [role]"
- "Create a JD for senior engineer"
- "Help me describe this position"

## Key Principles

### 1. Filter, Not Attract
- Be specific about what the role actually requires
- Describe the hard parts honestly
- Use language of target candidates, not HR compliance

### 2. Requirements vs. Nice-to-Have
- Maximum 3-5 genuine requirements
- Distinguish must-haves from preferences
- Avoid laundry lists that deter good candidates

### 3. The "Day in the Life"
- Describe actual work, not abstract responsibilities
- Include real challenges and expectations
- Set honest expectations about workload/pressure

## Data Structure

```json
{
  "jobs": [
    {
      "id": "JOB-12345",
      "title": "Senior Software Engineer",
      "level": "senior",
      "department": "Engineering",
      "location": "San Francisco / Remote",
      "employment_type": "Full-time",
      "salary_range": "$150k-200k",
      "created_at": "2024-03-01",
      "status": "active",
      
      "role_summary": "Build core platform infrastructure...",
      
      "requirements": [
        "5+ years production experience with distributed systems",
        "Deep expertise in at least one of: Python, Go, or Rust",
        "Track record of mentoring junior engineers"
      ],
      
      "responsibilities": [
        "Design and implement scalable backend services",
        "Review code and provide constructive feedback",
        "Collaborate with product on technical tradeoffs"
      ],
      
      "nice_to_have": [
        "Experience with Kubernetes",
        "Open source contributions",
        "Previous startup experience"
      ],
      
      "screening_criteria": [
        {"criterion": "Distributed systems experience", "weight": "high"},
        {"criterion": "Code quality in samples", "weight": "high"},
        {"criterion": "Communication clarity", "weight": "medium"}
      ],
      
      "challenges": [
        "High ambiguity in early-stage environment",
        "Rapidly evolving technical requirements",
        "Direct customer feedback loops"
      ]
    }
  ]
}
```

## Script Usage

```bash
# Create job posting
python scripts/create_job.py \
  --title "Senior Software Engineer" \
  --level senior \
  --department "Engineering" \
  --location "Remote"

# View job details
python scripts/view_job.py --job-id "JOB-12345"

# Update job
python scripts/update_job.py \
  --job-id "JOB-12345" \
  --field "salary_range" \
  --value "$160k-210k"

# List active jobs
python scripts/list_jobs.py --status active
```

## Output Format

```
JOB POSTING: Senior Software Engineer
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

THE ROLE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
We're looking for a Senior Software Engineer to build core 
platform infrastructure that processes 10M+ events daily. 
You'll architect distributed systems that scale with our growth.

This is not a role for someone who wants specs handed to them. 
You'll work with high ambiguity, make significant technical 
decisions, and own outcomes end-to-end.

WHAT YOU'LL DO
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
• Design and implement scalable backend services
• Review code and mentor junior engineers
• Collaborate directly with customers on technical issues
• Make architectural decisions with limited oversight

WHAT YOU NEED
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
• 5+ years production experience with distributed systems
• Deep expertise in Python, Go, or Rust
• Track record of mentoring junior engineers
• Comfort with high ambiguity and rapid change

NICE TO HAVE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
• Kubernetes experience
• Open source contributions
• Previous startup experience (<50 people)

THE HARD PARTS (Read This)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
• We move fast. Priorities shift weekly sometimes.
• You'll get direct customer feedback, positive and negative.
• We don't have dedicated ops - engineers own production.
• On-call rotation includes weekends.

IF THIS EXCITES YOU
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Apply with your resume and a brief note about a system you 
built that you're proud of and why.

DETAILS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Location: San Francisco or Remote (US timezones)
Salary: $150k-200k + equity
Reports to: CTO
Team size: 8 engineers

COMPENSATION
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Base: $150,000 - $200,000
Equity: 0.1% - 0.25%
Benefits: Health, dental, 401k matching
```

## Screening Criteria Template

For each job, define 3-5 specific criteria with weights:

```json
{
  "screening_criteria": [
    {
      "criterion": "Distributed systems experience",
      "description": "Has built and operated production distributed systems",
      "weight": "high",
      "evidence": "Resume mentions specific systems, scale metrics"
    },
    {
      "criterion": "Technical communication",
      "description": "Can explain complex concepts clearly",
      "weight": "high",
      "evidence": "Writing samples, documentation, clear resume"
    },
    {
      "criterion": "Language proficiency",
      "description": "Expert-level in required language",
      "weight": "medium",
      "evidence": "Code samples, projects, certifications"
    }
  ]
}
```

## Common Pitfalls to Avoid

### ❌ Don't
- List 15+ "requirements" (deters good candidates)
- Use generic corporate language
- Hide the hard parts (leads to early attrition)
- Copy from other companies without customization

### ✅ Do
- Be honest about challenges
- Write like you're talking to the ideal candidate
- Show personality and culture
- Update based on who's applying vs. who's succeeding


path: references/pipeline-tracking.md
content: # Pipeline Tracking

## Purpose
Track candidates through hiring stages with visibility, reminders, and next actions.

## When to Use
- "Update candidate status"
- "Where are we with [candidate]?"
- "What candidates need follow-up?"
- "Show me the pipeline"

## Pipeline Stages

### Standard Stages
1. **Applied** - Initial application received
2. **Screening** - Resume/application review
3. **Phone Screen** - Initial conversation (30 min)
4. **Technical** - Technical assessment or interview
5. **Onsite/Deep Dive** - Full interview loop
6. **Reference Check** - Contact references
7. **Offer** - Extend offer
8. **Hired** - Accepted offer
9. **Rejected** - Not moving forward
10. **Withdrawn** - Candidate withdrew

### Stage Durations (Targets)
| Stage | Target Duration | Alert If Exceeds |
|-------|----------------|------------------|
| Screening | 3 days | 5 days |
| Phone Screen | 5 days | 7 days |
| Technical | 7 days | 10 days |
| Onsite | 10 days | 14 days |
| Reference Check | 5 days | 7 days |
| Offer | 5 days | 7 days |

## Data Structure

```json
{
  "pipeline": {
    "candidates": [
      {
        "id": "CAND-456",
        "name": "Jane Doe",
        "email": "[REDACTED_SECRET]",
        "applied_at": "2024-03-01",
        "source": "LinkedIn",
        
        "current_stage": "technical",
        "stage_history": [
          {"stage": "applied", "entered_at": "2024-03-01", "exited_at": "2024-03-02"},
          {"stage": "screening", "entered_at": "2024-03-02", "exited_at": "2024-03-04"},
          {"stage": "phone_screen", "entered_at": "2024-03-04", "exited_at": "2024-03-08"},
          {"stage": "technical", "entered_at": "2024-03-08", "exited_at": null}
        ],
        
        "stage_deadline": "2024-03-15",
        "next_action": "Schedule technical interview",
        "action_owner": "Engineering Manager",
        
        "rating": "strong",
        "notes": "Great background, fast mover"
      }
    ]
  }
}
```

## Script Usage

```bash
# Add candidate to pipeline
python scripts/add_candidate.py \
  --job-id "JOB-12345" \
  --name "Jane Doe" \
  --email "[REDACTED_SECRET]" \
  --source "LinkedIn"

# Update candidate stage
python scripts/update_pipeline.py \
  --candidate-id "CAND-456" \
  --stage "technical" \
  --next-action "Schedule technical interview" \
  --owner "Engineering Manager"

# View pipeline for job
python scripts/view_pipeline.py --job-id "JOB-12345"

# Check for stale candidates
python scripts/check_pipeline.py --stale-days 7

# Set follow-up reminder
python scripts/set_reminder.py \
  --candidate-id "CAND-456" \
  --date "2024-03-12" \
  --note "Follow up if no response"
```

## Output Format

```
HIRING PIPELINE
Job: Senior Software Engineer
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

PIPELINE OVERVIEW
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Total Candidates: 23
Active: 12 | Rejected: 8 | Withdrawn: 2 | Hired: 1

STAGE BREAKDOWN
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📝 Applied:           3 candidates
🔍 Screening:         2 candidates  
📞 Phone Screen:      4 candidates
💻 Technical:         2 candidates
🏢 Onsite:            1 candidate
📋 Reference Check:   0 candidates
💰 Offer:             0 candidates

⚠️  STALE CANDIDATES (Action Needed)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

📞 Phone Screen Stage (>7 days)
• John Smith - In stage 9 days
  Next action: Send rejection or schedule technical
  Owner: Hiring Manager
  
💻 Technical Stage (>10 days)
• Sarah Johnson - In stage 12 days
  Next action: Complete debrief and decide
  Owner: CTO

ACTIVE CANDIDATES
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

🔥 STRONG CANDIDATES

Jane Doe - 💻 Technical (5 days)
  Rating: ⭐⭐⭐⭐⭐ Strong hire
  Next: Schedule technical interview (due 3/15)
  Owner: Engineering Manager
  Notes: Ex-Stripe, great distributed systems background

Michael Chen - 📞 Phone Screen (3 days)
  Rating: ⭐⭐⭐⭐ Likely hire
  Next: Move to technical
  Owner: Hiring Manager
  Notes: Good communication, solid fundamentals

📊 PIPELINE HEALTH
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

✅ On track: 8 candidates
⚠️  Stale: 2 candidates
🚨 Critical: 0 candidates

Average time in pipeline: 12 days
Target time to hire: 30 days
Current pace: On track

RECOMMENDED ACTIONS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Move Sarah Johnson to decision (stale 12 days)
2. Reject John Smith or schedule technical
3. Schedule technical for Jane Doe
4. Send follow-up to 3 candidates awaiting response
```

## Pipeline Health Metrics

### Key Metrics to Track
- **Time to hire** (application to offer accept)
- **Conversion rates** by stage
- **Source quality** (which sources produce hires)
- **Drop-off points** (where candidates exit)
- **Offer acceptance rate**

### Alerts
```
🚨 PIPELINE ALERTS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

• 2 candidates stale >10 days (risk of losing them)
• No candidates in Reference Check stage (bottleneck?)
• Average time to hire: 45 days (target: 30 days)
• Offer acceptance rate: 50% (industry avg: 70%)

Recommended: Review offer competitiveness
```

## Communication Tracking

Track all candidate communications:
```json
{
  "communications": [
    {
      "candidate_id": "CAND-456",
      "type": "email",
      "direction": "outbound",
      "subject": "Interview scheduling",
      "sent_at": "2024-03-10",
      "template_used": "interview_invite",
      "response_received": false,
      "follow_up_date": "2024-03-13"
    }
  ]
}
```

## Best Practices

### Speed Matters
- Respond to applications within 3 days
- Schedule interviews within 5 days of interest
- Make decisions within 2 days of final interview
- Send offers within 3 days of decision

### Communication
- Never ghost candidates
- Set clear expectations at each stage
- Provide timeline estimates
- Give feedback when possible (if candidate requests)

### Bias Prevention
- Use same questions for all candidates at each stage
- Evaluate against criteria before meeting
- Have diverse interview panels
- Document decisions with evidence


path: references/resume-screening.md
content: # Resume Screening

## Purpose
Evaluate candidates against job criteria to identify who is worth interviewing.

## When to Use
- "Screen this resume for the [role]"
- "Evaluate this candidate"
- "Should we interview this person?"

## Screening Process

### 1. Criteria Check
Evaluate against the 3-5 criteria defined in the job description:
- Must-haves (dealbreakers if missing)
- Key requirements (strong preference)
- Nice-to-haves (bonus points)

### 2. Evidence Assessment
Look for specific evidence, not claims:
- ❌ "Expert in Python" → ✅ "Built Python service processing 10M req/day"
- ❌ "Strong leader" → ✅ "Led team of 5, shipped X feature in Y timeline"
- ❌ "Results-driven" → ✅ "Reduced latency by 40% through Z optimization"

### 3. Red Flags
Watch for:
- Frequent job changes without progression
- Gaps without explanation
- Vague descriptions of accomplishments
- Claims not supported by evidence

## Data Structure

```json
{
  "screenings": [
    {
      "id": "SCR-789",
      "job_id": "JOB-12345",
      "candidate_id": "CAND-456",
      "screened_at": "2024-03-15",
      "screener": "Hiring Manager",
      
      "criteria_scores": [
        {
          "criterion": "Distributed systems experience",
          "score": 4,
          "max_score": 5,
          "evidence": "Built message queue at Stripe handling 50M messages/day",
          "notes": "Strong quantitative evidence"
        },
        {
          "criterion": "Technical communication",
          "score": 3,
          "max_score": 5,
          "evidence": "Resume is clear but cover letter is generic",
          "notes": "Should probe in interview"
        }
      ],
      
      "overall_score": 3.5,
      "recommendation": "interview",
      "confidence": "medium",
      
      "strengths": [
        "Strong technical background in relevant area",
        "Quantified impact in previous roles"
      ],
      
      "concerns": [
        "Hasn't worked in startup environment before",
        "Communication skills unclear from materials"
      ],
      
      "questions_to_probe": [
        "Tell me about a time you dealt with ambiguity",
        "How do you approach mentoring junior engineers?"
      ]
    }
  ]
}
```

## Script Usage

```bash
# Screen candidate
python scripts/screen_candidate.py \
  --job-id "JOB-12345" \
  --candidate-id "CAND-456" \
  --resume "resume.pdf"

# View screening result
python scripts/view_screening.py --screening-id "SCR-789"

# List screenings for job
python scripts/list_screenings.py --job-id "JOB-12345"

# Update screening
python scripts/update_screening.py \
  --screening-id "SCR-789" \
  --recommendation "reject" \
  --reason "Missing key requirement"
```

## Output Format

```
CANDIDATE SCREENING
Job: Senior Software Engineer | Candidate: Jane Doe
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

OVERALL ASSESSMENT
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Score: 4.2/5.0
Recommendation: ✅ INTERVIEW (High confidence)

CRITERIA EVALUATION
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

1. Distributed Systems Experience (High weight)
   Score: 5/5 ⭐⭐⭐⭐⭐
   Evidence: Built message queue at Stripe handling 50M messages/day
   Notes: Strong quantitative evidence, relevant scale

2. Language Proficiency - Python (High weight)
   Score: 4/5 ⭐⭐⭐⭐
   Evidence: 6 years Python, mentions asyncio and FastAPI
   Notes: Technical depth appears solid

3. Mentoring Experience (Medium weight)
   Score: 4/5 ⭐⭐⭐⭐
   Evidence: "Mentored 3 junior engineers" with specific outcomes
   Notes: Good evidence, should probe approach in interview

4. Technical Communication (High weight)
   Score: 3/5 ⭐⭐⭐
   Evidence: Resume clear, cover letter generic
   Notes: Unclear - probe in interview with written exercise

5. Startup Experience (Low weight)
   Score: 2/5 ⭐⭐
   Evidence: Only worked at large companies (Stripe, Google)
   Notes: Risk factor but not disqualifying

STRENGTHS ✅
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
• Strong technical background in core competency (distributed systems)
• Quantified impact in previous roles (metrics, scale)
• Evidence of mentorship with outcomes
• Career progression shows increasing responsibility

CONCERNS ⚠️
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
• No startup/small company experience
• Communication skills unclear - generic cover letter
• May expect more structure than we provide

QUESTIONS TO PROBE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. Tell me about a time you had to work with high ambiguity.
   → Assess startup fit

2. Describe how you approach mentoring junior engineers.
   → Validate mentoring claim

3. Write a brief technical explanation of [concept] for a non-technical audience.
   → Assess communication clarity

4. Why are you interested in joining a small startup now?
   → Assess motivation and expectations

NEXT STEPS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Recommended: Schedule technical phone screen
Priority: High (top 10% of applicants)
Timeline: Schedule within 3 days

⚠️  Note: This is a screening assessment only. Final hiring 
    decision requires interviews and reference checks.
```

## Scoring Guide

### 5 - Exceptional
- Exceeds requirement significantly
- Strong quantitative evidence
- Clear progression and impact

### 4 - Strong
- Meets requirement well
- Good evidence of capability
- Minor gaps only

### 3 - Adequate
- Meets basic requirement
- Evidence is present but limited
- Would need to develop in role

### 2 - Weak
- Below requirement
- Limited or unclear evidence
- Significant concerns

### 1 - Poor
- Does not meet requirement
- No relevant evidence
- Clear mismatch

## Recommendation Categories

- **Strong Interview** (4.5+): Top priority, schedule quickly
- **Interview** (3.5-4.5): Good fit, standard process
- **Maybe** (3.0-3.5): Borderline, consider phone screen
- **Reject** (<3.0): Does not meet requirements


path: scripts/add_candidate.py
content: #!/usr/bin/env python3
"""Add candidate to pipeline."""
import json
import os
import uuid
import argparse
from datetime import datetime

RECRUITING_DIR = os.path.expanduser("~/.openclaw/workspace/memory/recruiting")
PIPELINE_FILE = os.path.join(RECRUITING_DIR, "pipeline.json")

def ensure_dir():
    os.makedirs(RECRUITING_DIR, exist_ok=True)

def load_pipeline():
    if os.path.exists(PIPELINE_FILE):
        with open(PIPELINE_FILE, 'r') as f:
            return json.load(f)
    return {"candidates": []}

def save_pipeline(data):
    ensure_dir()
    with open(PIPELINE_FILE, 'w') as f:
        json.dump(data, f, indent=2)

def main():
    parser = argparse.ArgumentParser(description='Add candidate to pipeline')
    parser.add_argument('--job-id', required=True, help='Job ID')
    parser.add_argument('--name', required=True, help='Candidate name')
    parser.add_argument('--email', required=True, help='Candidate email')
    parser.add_argument('--source', default='Direct', help='Application source')
    
    args = parser.parse_args()
    
    candidate_id = f"CAND-{str(uuid.uuid4())[:6].upper()}"
    
    candidate = {
        "id": candidate_id,
        "job_id": args.job_id,
        "name": args.name,
        "email": args.email,
        "source": args.source,
        "applied_at": datetime.now().isoformat(),
        "current_stage": "applied",
        "stage_history": [
            {"stage": "applied", "entered_at": datetime.now().isoformat(), "exited_at": None}
        ],
        "rating": "pending",
        "notes": ""
    }
    
    data = load_pipeline()
    data['candidates'].append(candidate)
    save_pipeline(data)
    
    print(f"✓ Candidate added: {candidate_id}")
    print(f"  Name: {args.name}")
    print(f"  Job: {args.job_id}")
    print(f"  Stage: Applied")

if __name__ == '__main__':
    main()


path: scripts/create_job.py
content: #!/usr/bin/env python3
"""Create a new job posting."""
import json
import os
import uuid
import argparse
from datetime import datetime

RECRUITING_DIR = os.path.expanduser("~/.openclaw/workspace/memory/recruiting")
JOBS_FILE = os.path.join(RECRUITING_DIR, "jobs.json")

def ensure_dir():
    os.makedirs(RECRUITING_DIR, exist_ok=True)

def load_jobs():
    if os.path.exists(JOBS_FILE):
        with open(JOBS_FILE, 'r') as f:
            return json.load(f)
    return {"jobs": []}

def save_jobs(data):
    ensure_dir()
    with open(JOBS_FILE, 'w') as f:
        json.dump(data, f, indent=2)

def main():
    parser = argparse.ArgumentParser(description='Create job posting')
    parser.add_argument('--title', required=True, help='Job title')
    parser.add_argument('--level', choices=['junior', 'mid', 'senior', 'lead', 'exec'],
                        default='mid', help='Seniority level')
    parser.add_argument('--department', default='', help='Department')
    parser.add_argument('--location', default='Remote', help='Location')
    parser.add_argument('--type', default='Full-time', help='Employment type')
    
    args = parser.parse_args()
    
    job_id = f"JOB-{str(uuid.uuid4())[:6].upper()}"
    
    job = {
        "id": job_id,
        "title": args.title,
        "level": args.level,
        "department": args.department,
        "location": args.location,
        "employment_type": args.type,
        "status": "active",
        "created_at": datetime.now().isoformat(),
        "requirements": [],
        "responsibilities": [],
        "screening_criteria": []
    }
    
    data = load_jobs()
    data['jobs'].append(job)
    save_jobs(data)
    
    print(f"✓ Job created: {job_id}")
    print(f"  Title: {args.title}")
    print(f"  Level: {args.level}")
    print(f"  Location: {args.location}")
    print(f"\nNext steps:")
    print(f"  1. Add requirements: edit jobs.json or use --interactive")
    print(f"  2. Define screening criteria")
    print(f"  3. Post to job boards")

if __name__ == '__main__':
    main()


path: scripts/update_pipeline.py
content: #!/usr/bin/env python3
"""Update candidate stage in pipeline."""
import json
import os
import argparse
from datetime import datetime

RECRUITING_DIR = os.path.expanduser("~/.openclaw/workspace/memory/recruiting")
PIPELINE_FILE = os.path.join(RECRUITING_DIR, "pipeline.json")

def load_pipeline():
    if not os.path.exists(PIPELINE_FILE):
        return {"candidates": []}
    with open(PIPELINE_FILE, 'r') as f:
        return json.load(f)

def save_pipeline(data):
    os.makedirs(RECRUITING_DIR, exist_ok=True)
    with open(PIPELINE_FILE, 'w') as f:
        json.dump(data, f, indent=2)

def main():
    parser = argparse.ArgumentParser(description='Update candidate stage')
    parser.add_argument('--candidate-id', required=True, help='Candidate ID')
    parser.add_argument('--stage', required=True,
                        choices=['screening', 'phone_screen', 'technical', 
                                'onsite', 'reference_check', 'offer', 'hired', 'rejected'],
                        help='New stage')
    parser.add_argument('--next-action', help='Next action required')
    parser.add_argument('--owner', help='Action owner')
    parser.add_argument('--rating', choices=['strong', 'likely', 'maybe', 'no_hire'],
                        help='Candidate rating')
    
    args = parser.parse_args()
    
    data = load_pipeline()
    
    candidate = None
    for c in data.get('candidates', []):
        if c['id'] == args.candidate_id:
            candidate = c
            break
    
    if not candidate:
        print(f"Error: Candidate {args.candidate_id} not found")
        return
    
    # Update stage history
    now = datetime.now().isoformat()
    if candidate['stage_history']:
        candidate['stage_history'][-1]['exited_at'] = now
    
    candidate['stage_history'].append({
        "stage": args.stage,
        "entered_at": now,
        "exited_at": None
    })
    
    candidate['current_stage'] = args.stage
    
    if args.next_action:
        candidate['next_action'] = args.next_action
    if args.owner:
        candidate['action_owner'] = args.owner
    if args.rating:
        candidate['rating'] = args.rating
    
    save_pipeline(data)
    
    print(f"✓ Updated candidate: {candidate['name']}")
    print(f"  New stage: {args.stage}")
    if args.rating:
        print(f"  Rating: {args.rating}")
    if args.next_action:
        print(f"  Next action: {args.next_action}")

if __name__ == '__main__':
    main()


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)