I open-sourced my graduate job application toolkit
campus-apply is the semi-automated toolkit I built during fall graduate recruitment: shortlist employers, let AI fill out applications, and queue verification and final reviews on my phone. Here's what it does, what it doesn't, and how to set it up.
Before you start
If you'd rather skip the full post, give the prompt below to your AI coding assistant. Claude Code, Codex, or anything that can read and write files and run commands will do. You'll need a Mac. Replace the example in angle brackets with your own details, then paste the whole block:
Help me install campus-apply, an open-source framework for semi-automated
graduate job applications. For this session, stop at a ranked list of companies
that fit my preferences. Do not submit any applications.
1. Clone the repository: git clone https://github.com/arvakme/campus-apply-kit ~/campus-apply
Enter the directory and read AGENTS.md and README.md before making changes.
2. Use ~/campus-apply-instance for my personal instance directory. Create it if
needed. It must be outside the repository.
3. My preferences: <e.g. graduating in 2027, backend / Agent development,
Shanghai preferred over Hangzhou, also open to software test development>
4. Follow only L1 in docs/setup-checklist.md. Run each check first and use its
output to decide what to do. Skip anything that already passes. Do not
reinstall working tools or change my existing configuration.
5. Start with sample listings: cp -r data/sample data/paperball
These are 12 fictional companies. Leave real data sources for later;
adapters/sources/README.md explains how to connect my own.
6. Generate my intent.yaml and profile.md from templates/. Confirm each field
with me. Make me replace every placeholder with my own information.
7. When something needs me to act, give me one step at a time and wait for
me to say "done" before continuing.
8. Store passwords, identity document numbers, and API keys only in the instance
directory or macOS Keychain. Never put them in the repository or echo them
in the conversation.
9. Install automatic updates: uv run host/install-schedule.py install --role member
Then run status to confirm registration. This delivers upstream fixes
through an hourly git pull.
10. When finished, give me two things:
- log/battle-map.md in my instance directory: the candidate shortlist.
- The local dashboard: uv run --with pyyaml python3 web/app/server.py
Then open http://127.0.0.1:8787/board.
11. Finally, run python3 scripts/check_secrets.py --instance ~/campus-apply-instance
and confirm that it reports 0 matches.
Do not install Seedmux, ego lite, Devin, Tailscale, Bark, or Telegram at this
stage. Those belong to L2, automated applications. Wait until I've reviewed
the shortlist and said "start L2", then follow the L2 prompt in README.In about half an hour, you should have a list of companies ranked by your preferred roles and cities. Read on for how the automated applications work and where they stop.
The most time-consuming part of applying for graduate jobs in the fall is filling out forms. Every company has its own hiring portal, and every portal has its own application form: name, university, major, dates of study, willingness to consider other roles, awards. The same information, entered again on another page. Ten companies is manageable. By a hundred, you're numb.
During this round of applications, I turned that work into a toolkit called campus-apply. After using it to apply to a few hundred companies, I removed my personal information and open-sourced it.
How it works
Think of hiring a few interns to help with your applications. You give them a folder containing your resume and standard answers to common questions. Each gets a computer, opens a company's careers site, finds a suitable role, and fills out the form using that folder. When they reach something only you can do, such as scanning a login QR code, dragging a verification slider, entering an SMS code, or clicking the final submit button, they stop and message your phone: computer 3, this company, waiting for you to scan. Once you've handled it, they continue and record the result in a log.
In campus-apply, those interns are AI agents: programs that can operate a browser and command line themselves. The folder is a file called the answer bank. Messages arrive through Telegram or Bark push notifications.
The analogy breaks down in one place. A real intern will ask when something doesn't make sense; AI sometimes fills in the wrong answer with complete confidence. Several rules address this: read fields back after filling them, stop when unsure, and always pause before submitting to major employers so I can review the application myself.
Shortlisting employers
You write an intent.yaml describing the roles you want, such as backend or Agent development, your preferred cities, and roles to exclude outright, such as sales or chip design. The tool scores each graduate recruitment listing and produces a shortlist, with a web dashboard you can browse on your phone. I'll cover where the listings come from under "What it doesn't do."
Filling out applications
Each agent handles one company. It first checks whether you've already applied, then enters the portal, finds the best-fitting role, signs in, and fills out the form. Depending on the permission you've given it, it either submits or stops before submission. Several agents can work at once, each in a fixed browser window. After finishing one company, it clears the window for the next.
Answers come from exactly three sources: your personal profile, the answer bank, and your resume. If none of them covers a field, the agent stops and asks rather than making something up.
Some companies accept resumes only by email, without a portal. Those follow a separate path: draft the email using the subject format requested in the listing, check it, then send at a controlled rate. Messages are spaced a little over forty seconds apart, with some randomness and a daily cap to avoid being treated as spam.
Calling you in
Verification codes expire. With five agents working at once, five requests can arrive on your phone together, leaving you no time to deal with them.
A queue keeps at most 3 requests on your phone at a time; you can change that number. The rest wait. When a request reaches the front, its agent returns to the page, checks that the verification challenge is still there, takes a fresh screenshot, and sends it to you. Before sending, it also checks that the browser is still showing the correct company's page. I added that check after early versions kept sending me to a window for verification, only for me to find a different company's page open.
What it doesn't do
It doesn't solve verification challenges for you. Sliders, image selection, QR codes, SMS codes, and face verification are all yours to handle. There are anti-detection browsers that attempt to get around these checks, but I haven't integrated them. That would bypass the site's human verification and risk getting a job-seeker account banned.
It doesn't invent credentials. It won't add projects you haven't done or skills you don't have to make you fit a role. The email workflow has a specific check for phrases like "familiar with," "proficient in," and "a solid command of," because early AI drafts really did claim abilities that weren't on my resume.
It doesn't take assessments or written tests for you.
It doesn't ship recruitment data or scraping scripts. My own listings came from a members-only graduate recruitment board. Someone else compiled that content, so neither the data nor scripts for scraping that board are in the public repository. It includes only 12 fictional companies, enough to run through the workflow. For real data, convert a source you're entitled to use, such as your own spreadsheet or an export from your university's careers site, into the documented field format. The data stays on your computer.
You need to create your own Telegram bot. Ask @BotFather to create one; it takes a few minutes. Store the token in your own computer's keychain. My classmates and I share a bot, with a nightly leaderboard of application counts. That setup needs a small relay service of your own on Cloudflare. Its code is in the repository, but using it is optional.
Where personal information lives
Everything is split between two directories. The repository contains code, documentation, and notes on how to use each hiring portal. It can be public and updated with git pull. The instance directory holds your profile, answer bank, account details, resumes, and application log. It lives outside the repository and stays out of Git. Passwords and keys go only in macOS Keychain.
A check script reads sensitive values, such as phone and identity document numbers, from the instance directory and searches for each one in the repository. A match raises an error, guarding against accidentally committing personal information. Before open-sourcing this, I ran that check and then reviewed the files individually.
Setup
You'll need a Mac. Rather than typing commands from the documentation yourself, paste the prepared prompts into an AI coding assistant. The prompt at the top covers the first stage; README has the second. The assistant installs and checks things one at a time. Whenever you need to act, it gives you just one step.
Stage 1: get the shortlist
Allow about 30 minutes. Install a few basic tools (gh, uv, and Python), create the instance directory, fill in your preferences and profile, run the sample data through the filter, and open the local dashboard:
cp -r data/sample data/paperball # Start with fictional sample data
uv run host/scan.py # Build a shortlist from your intent
uv run --with pyyaml python3 web/app/server.py # Open http://127.0.0.1:8787/boardAt this point, you have a company list ranked by your preferences. You don't need to install anything else. Even applying manually from this list beats digging through WeChat posts one by one.
Stage 2: automate applications
This needs more tools. Most of the setup effort is here.
| Tool | What it's for |
|---|---|
| ego lite | The browser agents operate. They can reuse sessions where you've already signed in. |
| Seedmux | Runs several agent terminals at once, so the coordinating agent can distribute work. |
| An agent without per-token billing (I use Devin CLI) | The "interns" that fill out the forms. |
| himalaya | Command-line email, used to retrieve verification codes and send applications. |
| Tailscale, Bark, Telegram | View the dashboard, receive notifications, and press buttons from your phone. |
An agent without per-token billing is preferable for form filling. Applying to one company means repeatedly inspecting screenshots, which consumes a lot of usage. A classmate tried a token-billed model; the agent got stuck retrying one step for fifteen minutes and used up the entire allowance.
The most time-consuming part of stage 2 is filling in the answer bank. The tool has a hard rule: applications don't start until it's complete. Every unknown field forces the agent to stop and ask you, so more gaps mean more interruptions. My answer bank now has over a hundred and thirty entries, most prompted by questions I encountered while applying.
A few numbers
- On a 17-field Moka application, the batch-filling script finished in 31 seconds, leaving 2 fields for the agent to judge. An 83-field form for a state-owned company took 63 seconds, with 36 fields handled automatically. The rest were structured sections such as internships and projects; the script leaves those to the agent to check against the resume.
- From assignment to submission, the median time per company was about 16 to 19 minutes, excluding time spent waiting for me to handle verification. So far, the batch-filling script has cut only two or three minutes off that, and the sample is still small. Filling forms is a small part of the whole job. The rest is finding a role, signing in, checking details, and logging the result.
- A recruitment system's resume parser got my birth month and year wrong by nearly a year. I used to rely on the agent noticing and correcting it. Now the script compares every value imported from the resume with the answer bank and lists mismatches separately.
Limitations
- I've only run it on macOS, and several of its dependencies are Mac applications.
- It's semi-automated. Verification is still yours to handle, and applications to major employers still need your final review. You'll still deal with dozens of notifications a day. It mainly saves time filling out forms; you have to keep an eye on the notifications yourself.
- Hiring sites have different terms. Decide for yourself how much automation and how many daily applications are appropriate. I run at most 5 agents at once and apply only to the best-fitting role at each company.
- Portals change, and the operating notes in the repository can become outdated. The repository has a lessons log of actual problems I've hit. You can add new ones as you encounter them.
Glossary
- Agent: an AI program that can call tools, mainly a browser and command line here, to complete multi-step tasks.
- Answer bank: a list of questions a hiring portal might ask, with your standard answers alongside them. Agents consult it while filling out forms.
- Instance directory: the folder holding your personal data, outside the repository.
- Handoff: a step you must handle yourself, such as scanning a QR code, completing verification, or reviewing and submitting an application.
- L1 / L2: the two setup stages. L1 gets you a shortlist; L2 adds automated applications.
The repository is at github.com/arvakme/campus-apply-kit, under the MIT license. Good luck with your applications.