R

resumasher

by @earinov
4.0(20)

AIエージェントのスキルは、ユーザーのプロジェクト詳細に基づいて、特定の職務に合わせた履歴書のカスタマイズ、カバーレターの生成、および面接準備パッケージの構築を行います。ATSフレンドリーなPDFを出力し、学生や求職者が効率的に仕事に応募するのを支援します。

resume-buildercover-letterinterview-prepcareer-toolsai-agentGitHub
インストール方法
git clone https://github.com/earino/resumasher.git
compare_arrows

Before / After 効果比較

1
使用前

新しい仕事に応募するたびに、職務要件に合わせて履歴書を手動で修正し、パーソナライズされたカバーレターを作成し、ゼロから面接準備をする必要があります。このプロセスは時間と労力がかかり、重要な情報を見落としやすく、応募書類が職務内容と高度に一致していることを保証することが難しいため、就職活動の効率が低下します。

使用後

resumasherを使用すると、職務記述書を提供するだけで、プロジェクトファイルから証拠を自動的に抽出し、履歴書をカスタマイズし、カバーレターを生成し、面接準備パッケージを作成できます。これにより、大幅な時間短縮が実現し、応募書類が職務内容と高度に関連していることが保証され、就職活動の成功率が向上します。

SKILL.md

resumasher

Invoked as /resumasher <job-source> from inside the student's resume folder.

<job-source> is one of:

  • A path to a file containing the job description (job.md, jd.txt).
  • A URL to a job posting.
  • Literal text pasted after the command.

Optional flags: --style eu|us (override config default), --photo <path> or --no-photo (override config default).

Prerequisites

The skill requires Python 3.10+ with these packages (see requirements.txt): reportlab, pdfminer.six, chardet, nbconvert.

Workflow

Follow these phases in order. Every deterministic helper is available as a Python module under scripts/, and every LLM phase dispatches via the Task tool with subagent_type="general-purpose".

Setup: resolve paths in EVERY Bash tool call

⚠️ CRITICAL: Claude Code's Bash tool runs every command in a fresh shell. Variables set in one Bash tool call do NOT persist to the next. If you set SKILL_ROOT in one Bash call and reference "$SKILL_ROOT/..." in the next, $SKILL_ROOT will be empty and the command will fail with permission denied or file not found.

Every single Bash tool call that touches resumasher's code MUST begin with the path prologue below. It's short. Just paste it at the top of every command. Don't try to "remember" values from a prior call — they're gone.

The prologue (paste at the top of every Bash tool call):

SKILL_ROOT=""
NEEDS_INSTALL=""
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)"
for c in \
  "$HOME/.claude/skills/resumasher" \
  "$PWD/.claude/skills/resumasher" \
  "$REPO_ROOT/.claude/skills/resumasher" \
  "$HOME/.codex/skills/resumasher" \
  "$PWD/.codex/skills/resumasher" \
  "$REPO_ROOT/.codex/skills/resumasher" \
  "$HOME/.gemini/skills/resumasher" \
  "$PWD/.gemini/skills/resumasher" \
  "$REPO_ROOT/.gemini/skills/resumasher" \
  "$HOME/.opencode/skills/resumasher" \
  "$PWD/.opencode/skills/resumasher" \
  "$REPO_ROOT/.opencode/skills/resumasher"; do
  [ -n "$c" ] || continue
  [ -f "$c/SKILL.md" ] || continue
  if [ -x "$c/.venv/bin/python" ] || [ -x "$c/.venv/Scripts/python.exe" ]; then
    SKILL_ROOT="$c"; break
  else
    NEEDS_INSTALL="$c"
  fi
done
if [ -z "$SKILL_ROOT" ]; then
  if [ -n "$NEEDS_INSTALL" ]; then
    echo "ERROR: resumasher found at $NEEDS_INSTALL but its Python venv is missing." >&2
    echo "This means install.sh was never run after git clone. Fix:" >&2
    echo "  bash $NEEDS_INSTALL/install.sh" >&2
  else
    echo "ERROR: resumasher is not installed. See https://github.com/earino/resumasher#install" >&2
  fi
  exit 1
fi
RS="$SKILL_ROOT/bin/resumasher-exec"
TEL="$SKILL_ROOT/bin/resumasher-telemetry-log"
STUDENT_CWD="$PWD"

This sets:

  • SKILL_ROOT — absolute path to the installed skill (user-scope OR project-scope).
  • RS — absolute path to the bin/resumasher-exec wrapper that auto-locates the venv Python and the right script.
  • TEL — absolute path to bin/resumasher-telemetry-log, the no-op-when-tier-off event logger called at 8 pipeline boundaries below.
  • STUDENT_CWD — where the student is working (their resume folder, NOT the skill dir).

Telemetry identifiers you (the orchestrator) substitute literally: $MODEL and $HOST. Many "$TEL" calls below pass --model "$MODEL" and --host "$HOST". These are NOT shell variables the prologue sets — they're strings you substitute with literals before executing the command.

  • $MODEL: your own model identifier. Examples: claude-opus-4-7, claude-sonnet-4-6, gpt-5-codex, gpt-5-mini, gemini-2.5-pro, gemini-2.5-flash. You know what you are. If you genuinely don't, omit --model; null is better than fabricated.
  • $HOST: which AI CLI you're running in. Exactly one of claude_code, codex_cli, gemini_cli, or opencode_cli. You know this — it's literally the CLI that loaded this SKILL.md. If omitted, the log script falls back to env-var sniffing and then to "unknown", which is what we want to avoid.

Both are self-reported because bash can't reliably detect them across host CLIs (Codex, for instance, doesn't set a discoverable env var).

The check distinguishes three failure modes:

  • SKILL_ROOT set, success — everything good, proceed.
  • NEEDS_INSTALL set, SKILL_ROOT empty — skill was cloned but install.sh was never run. Error message names the exact command to fix it. This is the "future Claude cloned the repo and forgot the install step" case.
  • Both empty — skill isn't installed at all. Point the user at the README install section.

Every helper call in this document looks like:

"$RS" orchestration <subcommand> [args...]     # e.g., discover-resume, mine-context, company-slug
"$RS" render_pdf --input ... --output ...      # PDF rendering
"$RS" github_mine <username>                   # GitHub profile mine

The $RS wrapper handles three things for you: locating SKILL_ROOT by following its own path, execing the venv Python (not system Python — those dependencies aren't installed there), and picking the right script file. Do not run python -m scripts.orchestration or python scripts/orchestration.py directly; use $RS instead.

Run scratch files go in $STUDENT_CWD/.resumasher/run/ — NOT /tmp/. That directory is:

  • Already gitignored (the top-level .resumasher/ entry).
  • Scoped to the student's working folder, not system-global.
  • Wiped at the start of each run so prior scratch can't leak.

Create it once per run, near the top:

RUN_DIR="$STUDENT_CWD/.resumasher/run"
rm -rf "$RUN_DIR"
mkdir -p "$RUN_DIR"

Then every intermediate — resume text, folder context, sub-agent outputs — writes into $RUN_DIR/, not /tmp/.

Interactive prompt pattern (cross-host)

This skill runs on Claude Code, Codex CLI, Gemini CLI, and OpenCode. Each host has a different tool name but the same contract: present 2+ real options, let the student type free text in an "Other" field. The tools are:

  • Claude Code: AskUserQuestion
  • Codex CLI: request_user_input (NOT ask_user_question — that's an unshipped enhancement request)
  • Gemini CLI: ask_user
  • OpenCode: question

Wherever this document says "use the question tool" or names AskUserQuestion, use whichever tool your host provides. Reference them with backticks — models match fenced tool names more reliably than bare prose.

⚠️ All four tools require a MINIMUM of 2 real options. "Other" is auto-added and does NOT count toward the minimum. Supplying only 1 option crashes with InputValidationError: Too small: expected array to have >=2 items (Claude) or "request_user_input requires non-empty options for every question" (Codex). Gemini and OpenCode are similarly strict. This is the #1 first-run-setup bug to avoid.

Your job when collecting a free-text value is to avoid TWO separate mistakes:

  1. Passing only 1 explicit option (API error, nothing happens).
  2. Designing a middleman flow where round 1 asks "will you provide a value?" and round 2 actually collects it (API works, but doubles the prompts).

Both are avoidable with the right 2-option + Other shape.

Correct pattern A — when a default value exists (e.g., you extracted name / email / phone / linkedin / location from a resume.pdf):

Question: "Phone number for the resume?"
  A) Use the value from your resume: "+43 664 1234567"
  B) Skip — don't include phone on the tailored resume
  Other: paste a different phone number

Two real options (A = accept default, B = skip), plus Other for the student to override. One round, collects the value immediately.

Correct pattern B — when no default exists (e.g., GitHub username, photo path — the PDF doesn't contain these):

Question: "Do you have a GitHub? We can leverage it for this."
  A) I have one — paste the username/URL in Other below
  B) Skip — leave blank; set github_prompted=true so we don't re-ask
  Other: paste your GitHub username or profile URL

Two real options (A = I'll provide a value, use the Other field on this screen, B = skip permanently), plus Other for the actual value. Student picks "Other" in practice (since that's where the input is) — A exists purely to satisfy the minimum-2 constraint AND to give a visible hint that there IS an input field.

Wrong pattern 1 — 1 real option (API error):

Question: "Phone number?"
  A) Skip
  Other: paste your phone   ← InputValidationError, too few options

Wrong pattern 2 — middleman (2 rounds):

Round 1: "Phone number?"
  A) Skip
  B) I'll enter it          ← Student picks B
Round 2: "Type your phone number in Other field"
  A) (forced placeholder)
  Other: paste real value   ← Actual value arrives here

Doubles the prompts; the student could have pasted in round 1's Other directly.

Apply pattern A or B to every free-text collection: name, email, phone, location, LinkedIn, photo path, GitHub username.

No interactive tool available — hard-fail fallback

If none of the three question tools is available (e.g., codex exec non-interactive mode, a CI script run, or a host that doesn't yet ship any of them), do NOT guess values from context. Silent inference produced wrong configs for ambiguous inputs in v0.1 — students got run-time decisions they didn't make.

Instead:

  1. Stop before Phase 1.

  2. Write a skeleton .resumasher/config.json in $STUDENT_CWD with every required field set to the sentinel string "__ASK__". Include name, email, phone, linkedin, location, default_style, include_photo, photo_path, photo_position, github_username, and github_prompted: false.

  3. Print exactly this message to stdout, then exit with code 2:

    resumasher needs answers to its setup questions but this host does not
    support interactive prompts. Edit .resumasher/config.json, replace every
    "__ASK__" value with your real answer (use "" to skip optional fields
    like linkedin/photo_path), then re-run the skill.
    

This halt-and-resume path is the ONLY acceptable fallback. Never infer name, email, GitHub username, or style from resume content or JD location.

Sub-agent prompt pattern (cross-host)

Every LLM sub-agent resumasher dispatches (folder-miner, fit-analyst, company-researcher, tailor, cover-letter, interview-coach) uses a prompt built from runtime content — the student's resume, the folder summary, the JD, etc.

Do NOT build these prompts inline with string interpolation. A previous design had the orchestrator LLM substitute {resume_text} / {folder_summary} / {jd_text} tokens before dispatching. Cross-host testing revealed this is unreliable: under Gemini CLI, the fit-analyst sub-agent received a prompt with {resume_text} unfilled and produced a fit assessment that literally said "the resume section is a placeholder." Claude and Codex happened to substitute, but LLM judgment is the wrong tool for a mechanical string operation.

Instead, use build-prompt:

PROMPT=$("$RS" orchestration build-prompt --kind <kind> --cwd "$STUDENT_CWD" [--out-dir "$OUT_DIR"] [--company "$COMPANY"])

build-prompt reads the appropriate files from $RUN_DIR/ / .resumasher/cache.txt / $OUT_DIR/, substitutes them into the kind's template (defined in scripts/prompts.py), and emits the fully-rendered prompt to stdout. No LLM-side substitution, no ambiguity. If a required file is missing, build-prompt exits with code 2 and a clear error naming the file and the phase that produces it.

If a prompt is too large to round-trip through a shell variable (the folder-miner prompt routinely exceeds 100KB on a real GitHub mine, and some hosts cap argv length at 128KB), stage the rendered prompt to a file inside $RUN_DIR/prompts/ — NEVER /tmp/ — then read it back when dispatching:

mkdir -p "$RUN_DIR/prompts"
"$RS" orchestration build-prompt --kind folder-miner --cwd "$STUDENT_CWD" \
  > "$RUN_DIR/prompts/folder-miner.txt"
PROMPT=$(cat "$RUN_DIR/prompts/folder-miner.txt")

$RUN_DIR/prompts/ is gitignored (parent .resumasher/ is) and gets wiped at the start of every run, so prompt staging never leaks across sessions and never lands on the student's git history. /tmp/ is forbidden for prompt staging because: (1) on macOS it's world-readable to other local users until reboot, exposing the student's resume + JD + project content as plaintext PII; (2) prompt files there can outlive the run and accumulate across sessions; (3) we have no cleanup hook for /tmp paths the agent improvises. A defense-in-depth cleanup scan (Phase 9) catches and deletes any /tmp/<kind>-prompt.txt files that slip through anyway, but the SKILL.md prescription above is the first line of defense — please follow it.

Then dispatch the sub-agent with $PROMPT as the instruction text. Pass $PROMPT AS-IS — do not paraphrase, summarize, shorten, or rewrite it before dispatching. The compiled prompt has been carefully tuned per kind: it includes labeled <<<...BEGIN>>>/<<<...END>>> markers around resume, folder summary, JD, and company-research blocks; it includes prompt-injection defenses for UNTRUSTED content; it includes the exact ordering of structural instructions like "Start with a greeting H1" that downstream rendering depends on. A weak model that "improves" the prompt by handcrafting a shorter version (observed under qwen3.6-35b on OpenCode, run ses_235c — Qwen rewrote the cover-letter prompt and inverted "Start with" to "End with", causing the salutation to render as a giant H1 at the bottom of the PDF) ships broken artifacts that look superficially correct. The dispatch primitive AND the subagent_type value differ per host — use the entry that matches the CLI you're actually running in, not the first one listed. Picking the wrong subagent_type returns Unknown agent type: <X> is not a valid agent type and burns a dispatch attempt (observed under qwen3.6-35b on OpenCode, run ses_235c — the model defaulted to Claude Code's general-purpose and got rejected before self-correcting to OpenCode's general).

  • Claude Code: Task tool with subagent_type="general-purpose" and the prompt as description/prompt.
  • OpenCode: task tool (lowercase) with subagent_type="general" (NOT "general-purpose" — that's Claude Code's value) and the prompt as description/prompt. Same shape as Claude Code's Task. Note: same-message parallel dispatch works in current builds but has been historically flaky (sst/opencode#14195) — if two concurrent dispatches serialize instead of running in parallel, that's known and benign.
  • Gemini CLI: @generalist (its built-in generalist sub-agent).
  • Codex CLI: explicitly instruct the model to spawn a sub-agent — "spawn a sub-agent with the following prompt and return its output." Without the explicit spawn request, Codex tends to run the task inline in the parent session (still produces correct output,

...

ユーザーレビュー (0)

レビューを書く

効果
使いやすさ
ドキュメント
互換性

レビューなし

統計データ

インストール数5
評価4.0 / 5.0
バージョン
更新日2026年4月29日
比較事例1 件

ユーザー評価

4.0(20)
5
35%
4
35%
3
15%
2
10%
1
5%

この Skill を評価

0.0

対応プラットフォーム

🔧Manual

タイムライン

作成2026年4月27日
最終更新2026年4月29日