JobHunt with Hope

Home Tracker

Tracker — one spreadsheet, the whole hunt

One spreadsheet you own. Every role, its status, and the next action. Ask where you stand any time.

What your agent will do. Your agent keeps one spreadsheet of your whole hunt, on your computer: every role, its status, the next action and dated notes, with a backup before every change. Ask "where do I stand?" any time for a short answer, or ask for the weekly review: what moved, what is stuck, and what to do this week.

Open the AI you already use, point it at this page, and say: “where do I stand”


Hope Tracker · One sheet you own

job-tracker.xlsx is the live log of the whole hunt: one sheet, one row per role, owned by the user. This skill keeps its shape, answers “where do I stand?”, makes the changes the user asks for, and runs the weekly review. Every write goes through scripts/tracker.py, which backs the file up first.

Before you start

Find the plugin root:

# Find the Hope plugin root (references/, assets/, scripts/ live there).
# $CLAUDE_PLUGIN_ROOT is not expanded inside this Markdown, so resolve it in the shell.
# Works whether Hope is installed, marketplace-cached, or run with --plugin-dir, in bash or zsh.
PLUGIN_ROOT=""
if [ -n "$CLAUDE_PLUGIN_ROOT" ] && [ -f "$CLAUDE_PLUGIN_ROOT/plugin.json" ]; then PLUGIN_ROOT="$CLAUDE_PLUGIN_ROOT"; fi
if [ -z "$PLUGIN_ROOT" ]; then
  c="$(find "$HOME/.claude/plugins/cache/hope/hope" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort -V | tail -1)"   # newest cached version
  if [ -n "$c" ] && [ -f "${c%/}/plugin.json" ]; then PLUGIN_ROOT="${c%/}"; fi
fi
if [ -z "$PLUGIN_ROOT" ] && [ -f "$HOME/.claude/plugins/marketplaces/hope/plugin.json" ]; then PLUGIN_ROOT="$HOME/.claude/plugins/marketplaces/hope"; fi
if [ -z "$PLUGIN_ROOT" ]; then f="$(find "$HOME/.claude/plugins" -path '*hope*/plugin.json' -print -quit 2>/dev/null)"; if [ -n "$f" ]; then PLUGIN_ROOT="$(dirname "$f")"; fi; fi
echo "PLUGIN_ROOT=$PLUGIN_ROOT"   # check this before reading bundled files; empty means ask the user where Hope is

If PLUGIN_ROOT is empty, ask the user where the Hope plugin is. Do not guess. Check: ls "$PLUGIN_ROOT/scripts/tracker.py" finds the file. If not, that folder is an older Hope: ask the user where Hope 2.0 is installed. Then read:

  • $PLUGIN_ROOT/references/tracker-contract.md, all of it. It is this skill’s contract.
  • $PLUGIN_ROOT/references/hard-rules.md
  • $PLUGIN_ROOT/references/voice-guide.md (principle 6: every question is a set of choices)
  • In the hunt folder: user-story.md, HUNT-PROTOCOL.md (sections 4 and 5), applications/_common/profile.md (section 5, the per-company cap).

<hunt folder> below means the absolute path of the folder that holds job-tracker.xlsx and applications/.

What you make

  • Changes to job-tracker.xlsx, only through $PLUGIN_ROOT/scripts/tracker.py (init, add, update). The sheet Applications has eight columns, in this order: Status · Company · Role · Location · Band · Next action · Package · Notes.
  • A backup before every write, made by the script: job-tracker.backup-<YYYY-MM-DD>-<tag>.xlsx.
  • Answers in chat: where the user stands, and the weekly review. No markdown or CSV copy of the tracker is kept.
  • One dated line in user-story.md.

Steps

tracker.py below is short for python3 "$PLUGIN_ROOT/scripts/tracker.py". Always run step 1. Then pick the step for what the user asked: “where do I stand”, “what needs me”, “what’s live”, “show my tracker” or “how’s the hunt going” is step 2. A change is steps 3 and 4. A new role is steps 3 and 5. “Weekly review” is step 6, and “funnel” is its first part.

  1. Find the tracker. Run ls "<hunt folder>"/job-tracker.xlsx "<hunt folder>"/job-tracker.csv and pick the case (backup files do not count):
    • job-tracker.xlsx is there: go on.
    • Only job-tracker.csv (no .xlsx): openpyxl is missing, and the CSV is the live log. Add --file job-tracker.csv to every tracker.py command, and use job-tracker.csv in the notes view. Never run init while it exists: that starts a second, empty log. Tell the user once: python3 -m pip install openpyxl.
    • Neither, and applications/_common/profile.md is missing too: offer hope-onboarding (part 2). It sets up the whole hunt folder, the tracker included.
    • Neither, but the profile exists: run python3 "$PLUGIN_ROOT/scripts/tracker.py" init --hunt-dir "<hunt folder>".

    Check: python3 "$PLUGIN_ROOT/scripts/tracker.py" report --hunt-dir "<hunt folder>" exits 0 (with --file job-tracker.csv in the CSV case).

  2. “Where do I stand?” Run tracker.py report --hunt-dir "<hunt folder>", then the notes view below. The notes view prints each row with its status, role folder, the age of its newest note, and the start of its notes. Pass live to skip Rejected, Closed and Withdrawn rows, or all for every row.
date "+%A %d %B %Y"
python3 - "$PLUGIN_ROOT/scripts" "<hunt folder>/job-tracker.xlsx" live <<'PY'
import re, sys
from datetime import date, datetime
from pathlib import Path
sys.path.insert(0, sys.argv[1])
from tracker import read_rows
for r in read_rows(Path(sys.argv[2])):
    if sys.argv[3] == "live" and r.status.startswith(("Rejected", "Closed", "Withdrawn")):
        continue
    m = re.match(r"(\d{1,2} [A-Z][a-z]{2} \d{4}):", r.notes)
    age = (date.today() - datetime.strptime(m.group(1), "%d %b %Y").date()).days if m else "?"
    print(f"Row {r.number} | {r.status} | {r.company} | {r.role} | {r.package} | newest note {age} days ago")
    print(f"    {r.notes[:400]}")
PY

Answer in five to ten lines, in this order:

  1. Needs you: every row the report lists under “Needs you” (a next action addressed to the user, or a status of Interviewing, Offer, Inbound or Recruiter contact). Each gives the company, the role, the posting link (the **Posting:** line in the role’s JD.md) and the role folder path when the role has them, and the next action.
  2. Overdue: a next action whose date has already passed.
  3. Stale: a live row (not Parked) whose newest note is 14 or more days old, unless its next action names a date still to come.
  4. The counts, in one line: “3 roles: 1 shortlisted, 1 applied and confirmed, 1 interviewing.”

Check: every row the report printed under “Needs you” is in your answer.

  1. Before every write, the user’s own edits come first. They edit the sheet by hand too.
    • Check the file is not open: ls -a "<hunt folder>" | grep -E '^(~\$|\.~lock\.)job-tracker'. If it prints a name, say “Your tracker is open in Excel. Save and close it, then tell me.” and wait. A write that fails with “Permission denied” means the same.
    • Read the row first: tracker.py list --hunt-dir "<hunt folder>" --company "<company>". Change only the cells the user asked about, or that new evidence settles. A Status or Next action that no note explains was typed by the user, and it is theirs: ask before replacing it.
    • The script can only change Status, Next action, Package and Notes, and it only ever prepends to Notes, never replaces them. Company, Role, Location and Band are the user’s to edit by hand.

    Check: the lock check printed nothing, and you know the row’s current values.

  2. “Update the tracker” or “Mark X as Y.” One update per change, with a note that says what happened and who said so. Use --date when it happened on another day. Never type a date into the note text.
python3 "$PLUGIN_ROOT/scripts/tracker.py" update --hunt-dir "<hunt folder>" \
  --match "Contoso" --role "Senior Product Designer" --status "Rejected" --next "-" \
  --date 2026-10-13 --note "Rejection email, no reason given (Jane told Hope)"
  • Status words come only from the list in “Rules for this skill”. A detail may follow a spaced hyphen: Interviewing - round 2 on 15 Oct.
  • Applied only when the user names what they submitted. A confirmation email makes it Applied - confirmed.
  • Exit code 2: the match fits more than one row, or none. Read the candidates it printed and ask the user which one (AskUserQuestion, the candidates as options). Never guess.

Check: the output has Updated row <N>, a Notes now start: line with the new note first and one date, and a Backup: line.

  1. “Add this to my tracker.” Run tracker.py list --company "<company>" first. If the role is there, use update. Otherwise add with all eight columns:
python3 "$PLUGIN_ROOT/scripts/tracker.py" add --hunt-dir "<hunt folder>" \
  --company "Northwind Labs" --role "Design Engineer, Interfaces" --status "Shortlisted" \
  --location "Remote (US)" --band "not published" --next "You decide: apply or skip" \
  --package "-" --note "Jane added it from a LinkedIn post"
  • Package: run ls -d "<hunt folder>"/applications/*/. If a folder for this company and role exists, pass it (applications/northwind-design-engineer-interfaces/). Otherwise -.
  • Company, role, location and band come from the posting or the user. Missing: ask, never guess. Inside double quotes write every dollar sign as \$: --band "\$170,000 - \$200,000".

Check: the output has Added row <N>: <status> | <company> | <role> and a Backup: line.

  1. The weekly review. Run report, then the notes view with all. Give the user these parts, in order:
    1. The funnel. One table, then one line on how you counted (the counts come from the Status column and the notes):

      Stage Count the rows where
      Shortlisted every row (every role that entered the tracker)
      Applied the status starts with Applied, Interviewing or Offer, or a Rejected or Withdrawn row’s notes show the user applied
      Screened the status starts with Interviewing or Offer, or a Rejected or Withdrawn row’s notes record a call
      Interviewed the status or notes name a round after the first call (hiring manager, panel, round 2, onsite)
      Offers the status starts with Offer
      Rejected the status starts with Rejected
    2. What moved this week: rows whose newest note is 7 or fewer days old.
    3. Stale and overdue rows, as in step 2.
    4. The per-company cap: count the rows per company whose status starts with Ready to apply, Applied, Interviewing or Offer. Name any company at the cap in profile.md section 5 (default 2).
    5. A pattern worth facing, only after three or more rejections since the last review (grep -n "Weekly review" user-story.md shows its date). Read the rejection notes and any applications/<slug>/debrief-*.md fix lists. Say in plain words what the rejections share: the stage, the role shape, the reasons given. Then offer hope-discovery (change the sweep filters) and hope-interview (add it to the next prep). Write nothing into profile.md or HUNT-PROTOCOL.md without the user’s yes.
    6. What to close or park: a table (row, why, proposed status). Change only the rows the user picks, one update each.
    7. This week’s next actions: every next action addressed to the user, soonest date first.
    8. Protocol review: list the rows dated this week in HUNT-PROTOCOL.md sections 4 and 5. Ask: “Did you correct me on anything this week that is not written here?” If yes, write it there the same turn, dated, in the user’s words.

    Check: every part that applies is in the review (part 5 only after three rejections), and every change the user agreed to printed a Backup: line.

  2. Keep the notebook current. Add one dated line to “The journey so far” in user-story.md, for example - 14 Oct 2026: Weekly review: 3 tracked, 2 applied, 1 screened. Contoso parked. Skip it for a quick look that changed nothing. Tell the user in one line. Check: grep -nw "$(date '+%-d %b %Y')" user-story.md shows the line.

Rules for this skill

  • The workbook wins. If a markdown file (a status line in APPLICATION-ANSWERS.md, a list in user-story.md) disagrees with job-tracker.xlsx, fix the markdown. If the workbook looks wrong, ask the user before changing it.
  • Status words, exactly: Shortlisted · Ready to apply · Applied · Applied - confirmed · Interviewing · Offer · Rejected · Closed · Withdrawn · Recruiter contact · Inbound · Parked.
  • Applied is never a guess. Only on the user’s word, or a confirmation email (Applied - confirmed). A filled form is Ready to apply.
  • Writes go through tracker.py. If the script is missing, use the fallback in references/tracker-contract.md, section 8. It backs up first.
  • One live log. No CSV or markdown copy of the tracker is kept as a second truth. Reports live in chat.
  • No openpyxl: the script writes job-tracker.csv instead. Say the install line once: python3 -m pip install openpyxl.
  • Never delete a row, a note or a backup. To end a row, set Closed, Withdrawn or Parked.
  • Rule 4: never invent a fact. A note says what the user or a source said, and who said it.
  • Rule 14: the tracker never reaches an employer. Never attach it, and remind the user to close it before a screen share.
  • Rule 15: a change the user did not ask for (from the weekly review) is shown as a table first.
  • Rule 16: answer the question the user asked first.
  • Questions to the user go through AskUserQuestion (two to four options), or a plain yes/no.

What to tell the user when you finish

For “where do I stand”, the step 2 answer: needs you, overdue, stale, then the counts. For the weekly review, the eight parts of step 6, each short.

Example (Jane Doe, Wednesday 14 Oct 2026):

Where you stand, Wed 14 Oct

Needs you

  • Acme Design Systems, Senior Product Designer. https://jobs.ashbyhq.com/acme-design-systems/7c1e2f40-5a6b-4c3d-9e8f-0a1b2c3d4e5f · applications/acme-senior-product-designer/. Overdue: Sam’s hiring manager slots were due 7 Oct and have not come, and your plan was one nudge after 9 Oct. Want a nudge draft?

Stale: none. Contoso has been quiet for 19 days, but its next action waits until 16 Oct.

3 roles: 1 shortlisted, 1 applied and confirmed, 1 interviewing.

Done when

  • The sheet matches what the user told you. Every change printed Updated row or Added row.
  • Every write printed a Backup: line, and the backup file exists.
  • The answer or the review is out, with the “Needs you” rows first.
  • No second copy of the tracker was made.
  • user-story.md has today’s line if anything changed.

Next

  • Every day: hope-sync, so the rows follow the inbox. Offer: “Want me to check your email and LinkedIn now?”
  • No Shortlisted rows left: hope-discovery, for a fresh sweep.
  • A nudge or a reply is due: hope-write.