agentleFS
AkbarDevopAkbarDevop/ai-job-agent/skills/job-triage/SKILL.md

job-triage

Skill61 starsChanged 6 months agoMITView on GitHub

Search and classify job-related emails in Outlook Web — rejections / interview invites / action required / confirmations / recruiter outreach / other. Proactively invoke this skill (do NOT answer conversationally) when the user asks "check my inbox", "check Outlook", "any replies", "triage my inbox", "any rejections", "any interview invites", "what emails came in", "did anyone respond to my applications", or invokes /job-triage.

We split this skill into the rules it gives an agent

11 rules. About 0 are common, 5 are distinctive, and 6 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈5

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Before doing anything, verify Chrome is reachable:

    line 20
  2. Very specific:

    If MISSING, tell the user:

    line 26
  3. Distinctive:

    Stop until they confirm.

    line 38
  4. Very specific:

    Default query: application status.

    line 49
  5. Very specific:

    Run each text through these regex rules in order — first match wins:

    line 55
  6. Very specific:

    Safety: if the sender contains @missouri.edu or @umsystem.edu (and not mailer/noreply/bot), force class = other — this matches the …

    line 66
  7. Distinctive:

    Keep each row ≤ 120 chars.

    line 87
  8. Very specific:

    After classifying the inbox, read $AI_JOB_AGENT_ROOT/outreach-log.csv and try to match each unread email to a prior cold-email row by …

    line 91
  9. Distinctive:

    If they pick extract:

    line 136
  10. Very specific:

    If they pick mark-read, loop through the indices of confirmation class only (never mark interview_invite, action_required, or …

    line 142
  11. Distinctive:

    After each action, re-run the search to get fresh indices (the script uses positional indexes into the current result list).

    line 148

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Job Triage

Thin wrapper around scripts/outlook-triage.js that runs a search, classifies results locally, and presents them as a table. The underlying script requires a Chrome instance running with --remote-debugging-port=9224 and Outlook Web already logged in.

Repo location

Same resolution as /job-apply: $AI_JOB_AGENT_ROOT → ~/.claude/skills/ai-job-agent/ → REPO_PATH marker file → ~/ai-job-agent/.

Prerequisites

Before doing anything, verify Chrome is reachable:

curl -s http://127.0.0.1:${OUTLOOK_PORT:-9224}/json/version >/dev/null && echo OK || echo MISSING

If MISSING, tell the user:

Start Chrome with remote debugging and open Outlook Web first:

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
  --remote-debugging-port=9224 \
  --user-data-dir=/tmp/outlook-triage-profile

Then navigate to outlook.office.com and log in. Rerun /job-triage.

Stop until they confirm.

Workflow

1. Run the search

cd "$AI_JOB_AGENT_ROOT"
node scripts/outlook-triage.js --port "${OUTLOOK_PORT:-9224}" search "$QUERY"

Default query: application status. If the user passes a different query via $ARGUMENTS, use that instead.

The script returns a JSON array of result objects with idx, aria, text, unread, flagged, pinned.

2. Classify each result

Run each text through these regex rules in order — first match wins:

ClassRegex (case-insensitive)
interview_invite`interviewschedule a (call|time)meet withphone screenpanelavailable`
action_required`assessmentaction requirednext stepscomplete yourfinish your app`
rejection`unfortunatelynot moving forwardother candidatesno longer being consideredpursuing otherposition has been filleddecided not to`
confirmation`received your applicationthank you for applyingapplication has been submittedwe have received`
recruiter_outreach`recruitertalent acquisitionwould love to connectexplore opportunit`
other(default fallback)

Safety: if the sender contains @missouri.edu or @umsystem.edu (and not mailer/noreply/bot), force class = other — this matches the Mizzou-human safeguard already in the Outlook pipeline.

3. Render counts table

ClassCountUnread
💼 interview_inviteNN
⏰ action_requiredNN
❌ rejectionNN
✅ confirmationNN
👤 recruiter_outreachNN
💤 otherNN
totalNN

4. Render detail table (first 20)

#ClassUnreadSubject / preview
0❌ rejection●"Regarding your application at Acme — unfortunately..."
1💼 interview_invite●"Phone screen — 30 min Tuesday at 2pm CT?"

Keep each row ≤ 120 chars.

4.5. Cross-reference with outreach-log.csv (auto-detect replies)

After classifying the inbox, read $AI_JOB_AGENT_ROOT/outreach-log.csv and try to match each unread email to a prior cold-email row by sender email (the to_email field in outreach-log is the recipient of the original send — when they reply, they become the sender of the inbound email).

Extract the sender email from each result's aria label or extracted body (use outlook-triage.js extract <idx> if needed for the headers). Match against outreach-log.csv.to_email (case-insensitive).

For every match where the row's replied_at is currently empty:

Original outreach (from log)Inbound classProposed update
Paul Young paul@gft.com · sent 2026-04-10interview_invitereplied_at = today, status = interview_scheduled
Zulfiya V zv@worley.com · sent 2026-04-15rejectionreplied_at = today, status = replied, append note
Hans M h.mueller@siemens.com · sent 2026-04-22recruiter_outreachreplied_at = today, status = replied

Render the proposed updates as a confirmation table:

🔁 Reply auto-detection — 3 matches in outreach-log.csv

| # | Recipient            | Original sent | Inbound class      | Proposed status     |
|---|----------------------|---------------|--------------------|---------------------|
| 1 | paul@gft.com         | 2026-04-10    | interview_invite   | interview_scheduled |
| 2 | zv@worley.com        | 2026-04-15    | rejection          | replied             |
| 3 | h.mueller@siemens... | 2026-04-22    | recruiter_outreach | replied             |

Apply all? Apply some (numbers comma-separated)? Skip?

On confirm, edit outreach-log.csv directly via the Edit tool — find each row by to_email, set replied_at to the current ISO timestamp, set status to the proposed class, and append a one-line note to notes. This skips /job-status because outreach-log is a different file from application-tracker.csv (and /job-status operates on the latter).

Print a summary:

FieldValue
Matches found3
Updates applied3
Rows still pending(count of unmatched inbound emails — those are likely from contacts not in your outreach log)

Skip this step entirely if outreach-log.csv doesn't exist (cold-email setup wasn't completed).

5. Offer next actions (don't do them automatically)

Ask the user:

  • "Extract one to read in full? (pass an index)"
  • "Mark the confirmations as read? (all / one / none)"

If they pick extract:

node scripts/outlook-triage.js --port "${OUTLOOK_PORT:-9224}" extract <idx>

If they pick mark-read, loop through the indices of confirmation class only (never mark interview_invite, action_required, or recruiter_outreach as read — those need a human eye):

node scripts/outlook-triage.js --port "${OUTLOOK_PORT:-9224}" mark-read <idx>

After each action, re-run the search to get fresh indices (the script uses positional indexes into the current result list).

Rules (from root CLAUDE.md)

  • Mark confirmation emails as read after logging. Leave interviews, assessments, and recruiter outreach unread.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.