Jobforge
2026 · open source · claude code plugin
The coding-interview tools I tried focused on the submitted code. I wanted help with the part before that, when I said “I'll DP this” and started typing. Jobforge is a Claude Code plugin that asks for the plan first and grades that. It also keeps my résumé, target roles, lessons and interview debriefs in local Markdown files.
Figure 01 · The drill loop
One file read, one plan graded, one row written
The hook reads rep-log.md and nothing else. The drill picks what bank.md says is due, generates from the pattern's discriminator, grades the spoken plan against that pattern's required elements, and writes one row with verdict, missing element and due date. A debrief of a real interview writes failures into the same bank.
- Reads
- The plan, out loud
- Row written
What it is
It runs as a plugin with one SessionStart hook, six skills, sixteen pattern files and a Python harness for running solutions. State is markdown in ~/jobforge that you can read, edit and remove. I built it mid-prep, interviews on the calendar, from a private version that had run for a few weeks. I chose the initial settings from my practice log.
/jobforge:drill generates a problem for a due pattern and asks what you would do before you write anything. /jobforge:interview-debrief records an interview you sat, with the same vocabulary, and queues whatever broke. /jobforge:status shows the streak and which element keeps failing. /jobforge:archive is the intended ending: past your target date, it stops.
Explaining the plan before coding
I read the source of six tools before building this. Their mistake classifiers worked from submissions. I wanted to check whether I could explain the solution before typing it, including cases where I could produce correct code without explaining why it worked.
Each pattern declares three to five required elements from a global list of eleven: base case seeded, transition stated as a formula, iteration order justified, and so on. The drill marks each one present, vague or missing and must quote the part of your explanation that supports its mark. “I'll build up the table” is a vague transition. It does not explain the recurrence or the order in which to fill the cells.
/jobforge:drill · grading
> Before you write anything: what are you going to do, and why that? pattern dp-2dstate-definition present "dp[i][j] is the best score using the first i and j"base-case present "row zero and column zero are all zero"transition vague "then I fill in the table"iteration-order missing - verdict: failediteration-order was missing. You said "then I fill in the table" —fill it in which direction, and what does each cell need already computed?
bank.md · one row per rep
| date | pattern | verdict | missing | due ||------------|-----------------|---------|-----------------|------------|| 2026-08-27 | monotonic-stack | half | iteration-order | 2026-08-30 |
The same element IDs are used across patterns. A missing base-case on dp-2d and a missing base-case on prefix-sum land in the same column, so after sixty days /jobforge:status can say one seed was missed on five unrelated patterns. The missed element is also what gets scheduled: the next rep is a different pattern that depends on it, never the same problem three days later.
One banner, one subject
The hook prints one line at session start when you have not drilled today. That is the plugin's only unsolicited reminder. It reads rep-log.md and nothing else. No code path leads from it to the résumé, the target list or the bank, so it cannot nag about them. I did not want it also telling me your LinkedIn headline is stale, when I had opened the tool to practise.
A test checks that the hook stays within those limits. tests/test_push_pull_boundary.py strips the comments and asserts the hook contains exactly one os.path.join, one open(, one markdown filename, no networking imports, and no mention of resume, targets, profile, bank or interviews. An extra file read would fail that check.
hooks/drill-banner.py
REP_LOG_FILENAME = "rep-log.md" # the only filename this process may ever open def rep_log_path() -> str: """The only path-producing function in this module.""" return os.path.join(jobforge_home(), REP_LOG_FILENAME) def _read() -> str: path = rep_log_path() if os.path.basename(path) != REP_LOG_FILENAME: return "" ...
session start
🔨 3-day streak, not yet logged today. /jobforge:drill Median session 24 min — the floor is 20.
The same tiering runs inside the skills. The drill reads the rep log, the bank and the pattern files. It never opens the résumé: a tool picking a graph problem has no reason to, and you cannot audit what it never opened. Nothing in the plugin sends anything anywhere, so there is no telemetry setting. The README also notes that if your employer manages the machine, put JOBFORGE_HOME on a personal volume.
- form
- Claude Code plugin · 1 SessionStart hook · 6 skills
- patterns
- 16 · 8 Tier A, 8 Tier B
- element ids
- 11, global across patterns
- verdicts
- failed · half · coded · named-clean
- intervals
- +3 days / +14 days, written into the row
- tests
- 35, unittest, all deterministic
- runtime
- python 3 · stdlib only · 0 dependencies
- state
- markdown in ~/jobforge, no telemetry
- tracked files
- 53
- status
- v0.1.0 · MIT · derived from swe-interview-coach
Where I kept the scope small
It does not capture submissions. A browser extension sits at the moment you hit submit and can interrupt you. A CLI agent exists only when invoked. Several extensions already do auto-capture with FSRS scheduling well, and I read them before deciding not to compete there. I kept this plugin focused on the spoken plan, which is the part I wanted an agent to review.
The plugin does not ship a problem bank or save generated questions. Problems come from the pattern's discriminator, never from a title, re-skinned to your own domain. The state files hold a pattern, a verdict, an element id and a date. I only keep the practice record.
The system-design references, the harness and the Excalidraw canvas are derived from swe-interview-coach under MIT, with attribution per file. The taxonomy, the grading mechanism, the hook, the bank and the interview schema are new. Every constant generalises from one person's log, so it sits in frontmatter and is meant to be changed.
// NEXT
Groundplane
An agent boundary you can inspect