skip to content

terminalhire

What's new

Every release of the terminalhire CLI and Claude Code plugin, newest first. Your profile has never left your machine — since day one.

v0.40.11 — 2026-07-29

The in-session founder posting release now includes the current claim lifecycle from main, so the npm package and Claude Code plugin ship one identical tree.

  • Keeps the v0.40.10 local posting-draft and founder-surface behavior.
  • Includes stalled-claim release and live CLI claim-approval sync from TERM-273/TERM-287.
  • Rebuilds the committed plugin after the mainline integration so its source hash matches the package published to npm.

v0.40.10 — 2026-07-29

Founders can draft postings from an active coding session without giving an agent permission to publish or spend money.

  • terminalhire post draft captures a local, private, editable posting draft with branch, remote, changed-path, stack, command, output, and session-summary provenance. Drafting makes zero network calls.
  • Draft storage refuses symlinked state directories, secret-shaped content, home-directory paths, and incomplete capture. Files use the existing secret-state policy, atomic writes, and owner-only permissions.
  • post submit is a human-only, typed-confirmation handoff to the existing browser confirmation flow. The browser remains the only place that can publish, choose scope and price, connect GitHub, or attach payment policy.
  • Linked founders get a derived, overrideable founder lead in bare jobs and bounties commands. The statusline carries counts only; private posting titles, repositories, and claimant identities never enter it.
  • Developers can see whether a founder-authored spec was agent drafted and then explicitly confirmed by the founder.

v0.40.9 — 2026-07-28

Paid founder claims now complete the developer notification path at claim time, and review feedback points to a transition that can actually clear it.

  • A successful paid founder claim stores a push-only token encrypted on the developer's machine, enables the existing score-free claim sync, and says how to revoke it. If that enrollment cannot finish, the claim remains recorded and the CLI prints the exact recovery command.
  • terminalhire claim status [<id>] now checks founder approvals on demand and joins claim ids to the local claim ledger. It still polls public PR state for open-source work.
  • With no stored token or during an approvals outage, claim status shows local state and an enrollment or retry hint instead of failing or erasing state.
  • A revise verdict now points to the existing claim update <id> ready transition only after its blockers are fixed and review is rerun.
  • Marking that claim ready clears the stale revise verdict, so the next claim submit <id> is accepted instead of repeating the same refusal.
  • The list guidance and submit refusal share one command, and tests exercise the claim's full lifecycle-plus-review state so the two surfaces cannot drift.

v0.40.8 — 2026-07-28

Founder bounty payments now say what actually happens.

  • Saving a card, posting, claiming, approving, submitting, and rejecting work do not create a charge.
  • Accept & pay charges the posted gross amount. Acceptance and payout are recorded only after Stripe confirms payment.
  • The developer receives 90%; TerminalHire retains 10% and absorbs Stripe's processing fee.
  • A saved card is not escrow or a guarantee that a later charge will succeed.
  • Scraped and external open-source bounties keep their source platform's payment terms; TerminalHire takes no cut from them.

v0.40.7 — 2026-07-28

Nothing you can see changed. This closes the last version of the 0.40.5 consent bug — one that no code path could reach yet.

  • The statusLine consent prompt now refuses anything that is not a typed yes. 0.40.6 fixed the bug by excluding the one bad value it had been shown. That left the next one open: a prompt returning nothing at all would still have installed, for the same reason Ctrl-D did. The check now asks what the answer is rather than listing what it must not be, so a prompt added later cannot bring the bug back in a new costume.

v0.40.6 — 2026-07-28

Fixes a consent bug shipped hours earlier in v0.40.5. Upgrade past 0.40.5.

  • Ctrl-D at the statusLine prompt no longer counts as yes. Making the prompt default to yes in 0.40.5 was written as "install unless the answer is n or no", and the prompt returns an empty answer when the input closes — so Ctrl-D, a closed terminal, or quitting terminalhire init partway through all read as consent and wrote ~/.claude/settings.json. So did nope, cancel, and any answer the two-letter test did not recognise. It now installs only on y, yes, or a bare Enter, and declines everything else. If 0.40.5 enabled a statusLine you did not ask for, remove it with terminalhire statusline --off — your previous statusLine is restored from the backup it made.
  • The update notice compares the right package. It read the first package.json it found with a version field, which in the plugin layout could have been the plugin's rather than the CLI's.

v0.40.5 — 2026-07-28

The statusLine is on by default, tells you when an update is waiting, and one command stops repeating itself sixteen times.

  • Connection notifications are on by default. The setup prompt used to demand the literal word yes — Enter declined, y declined — which made it the strictest step in onboarding, one below a prompt that sends a credential and accepts Enter. The reflex way to dismiss a prompt turned it off, so the surface carrying every notification was off for people who thought they had accepted it. It now reads [Y/n] with Enter meaning yes. The disclosure is still printed in full, an existing statusLine is still preserved and backed up, and anything that is not y, yes or a bare Enter declines — including Ctrl-D. A non-interactive stdin keeps the old bar: only the literal word yes enables it there, so a pipe or a CI runner cannot turn it on by sending a newline.
  • An available CLI update shows in the statusLine. The plugin auto-updates; a globally installed CLI does not, and the check that knew you were behind only ever said so in refresh output. It now renders as ⬆ terminalhire 0.40.4 → 0.40.5 — run: th update, last in the line, because people and payments outrank the tool. No new network call: the published version already rides in the index the CLI downloads.
  • A command no longer prints the same warning sixteen times. When the OS keychain is unavailable, the notice was emitted on every single read and write of the encrypted store, burying the output of whatever you ran. It is said once.

v0.40.4 — 2026-07-27

claim list now tells you what to type next.

  • Every claim that can move shows the one command that moves it. The list printed a state and an id and stopped there, which told you where a claim was and nothing about how to advance it. A Next: block under the accepted-PR rate now names the command per claim. A freshly claimed founder posting points at claim slice, which is what hands you the work; an open-source claim points at claim start, which forks and clones. Work in progress points at claim submit, except from in-review, where submit refuses — that one points at claim update <id> ready first. A submitted claim points at claim runs --watch for founder work and claim status for everything else. Merged and abandoned claims say nothing, because there is nothing to do.

v0.40.3 — 2026-07-27

Paid work waiting for you now announces itself, and it arrives sooner.

  • A fresh install no longer marks every waiting bounty as already read. With no badge history, the 💎 N paid count treated the whole open board as seen and showed nothing. It was unrecoverable, too — the seen set only ever shrinks, so no later refresh could surface those postings. Install while paid work is open and you were simply never told. Missing history now means nothing has been seen, which is what it always meant.
  • An unchanged refresh stops re-downloading the index. The CLI now sends If-None-Match, so a poll that learns nothing changed costs a header exchange instead of ~378KB. A stale validator can never strand you: if the server says "unchanged" about something the cache does not hold, the tick re-fetches in full.
  • The background refresh ticks every 2 minutes instead of every 10. Affordable only because of the change above. TERMINALHIRE_REFRESH_INTERVAL still overrides it, in seconds.

A posting now reaches your status line in up to 7 minutes rather than up to 15, and typically in 3–4. The remaining delay is the 5-minute edge cache on the index, not the refresh loop.

Why 0.40.3 and not 0.40.2. terminalhire@0.40.2 was already published to npm, from a tree that contains none of the above — its tarball is byte-identical to 0.40.1 apart from the version string in package.json. npm versions are immutable, so this ships as 0.40.3 rather than silently never reaching anyone. If you installed 0.40.2, you have 0.40.1's code under a different number; upgrade.

This is the second time in three releases (see 0.40.1 below). The lockstep guard checks that our seven files agree with each other and cannot see the registry at all, so a number burned outside the repo passes every check we run.

v0.40.1 — 2026-07-27

Fixes two defects in the v0.40.0 badges, both found on review before the release reached anyone through the plugin channel.

  • ✅ N approved no longer under-counts. Collecting the code slice for an open-mode claim used to clear one approval from the badge, even though open-mode claims are never counted in it — nothing needs approving. Only approval-only claims now acknowledge the badge.
  • A server outage no longer looks like "no approvals". /api/claim/approvals answered a store failure with an empty list and a 200, which the CLI read as authoritative and used to discard which approvals it had already collected. Every approval then reappeared on the next successful refresh. The route now answers 503 when it cannot reach the store, and the CLI keeps its last known count.

Why 0.40.1 and not a re-publish of 0.40.0: terminalhire@0.40.0 was published to npm ~33 minutes before the first of these fixes was written, so the tarball on the registry does not contain them. npm does not allow republishing a version, and two different artifacts must not share one version number. 0.40.0 remains on npm and is superseded; the plugin channel never shipped it.

v0.40.0 — 2026-07-26

Two things a developer was never told: that a founder approved their claim, and that paid work had appeared.

Your statusline says when a founder said yes

Approve a developer's claim on a private posting and, until now, nothing reached them. The approval notice was written for the founder's ladder, which excludes it on the reasonable-sounding grounds that they already know — they clicked the button. The developer, who is the one waiting, had to keep re-running terminalhire claim slice <id> until the refusal turned into a slice.

The statusline now carries ✅ N approved, and it behaves like an obligation rather than a notification: it does not fade after one tick, and looking at it does not clear it. Fetching the slice does, because that is when you actually have the work.

Needs terminalhire claim --push --keep-updated — checking on your behalf means a request with your credential, which stays your decision. If you have not enrolled and a claim is waiting on a founder, refresh now says so, reading only your local claim file and sending nothing.

And when there is paid work you could take

💎 N paid joins it, for first-party postings a founder has put money behind. It counts what is NEW since you last looked rather than what exists, so it goes quiet on its own; terminalhire bounties is what marks it read, and only for the rows that actually printed. Scraped bounty-board supply is deliberately not included — nobody is waiting on you for those.

Notes

  • Neither badge can carry a repo name, title or price. Both read a count, and the statusline has no path to the underlying list.
  • leads 💎, which leads your messages: work granted to you by name outranks work anyone could take, which outranks a conversation.

v0.39.9 — 2026-07-26

The developer side of a founder posting opens: claim, slice, patch, runs.

A founder-posting claim is registered, not just noted

claim record on a founder posting now registers the claim server-side, bound to your browser-verified GitHub identity — that registration is what a later paid credential rests on. On an approval-only posting the claim waits in an approval-pending state until the founder approves you; nothing is wrong and there is nothing to retry. Contention stays real: an open posting can hold claims from several developers, and registering does not lock anyone else out.

Read the slice, send the patch, read their CI — without ever holding the repo

Three verbs close the loop on a private repo you never clone:

  • claim slice <id> — the files the founder confirmed, contents included.
  • claim submit <id> — on a founder posting this submits a patch over HTTP instead of opening a PR from a fork. The platform applies it to a branch in the founder's repo with your commit authorship; a patch touching a file outside the slice is refused. This call always asks for a fresh browser verification — it is the one action that writes to someone else's repository under your name, so the persistent background-push credential is never accepted for it.
  • claim runs <id> — the founder's own CI verdict on your branch: passed, failed, or still running.

Founder bounties lead the ranked surfaces

A live, claimable founder bounty now pins above roles in bounties output and the ambient tip rotation. Finished work no longer advertises: a posting whose work was accepted leaves the supply instead of ranking forever, and an approval-only posting already assigned to someone is listed but never pinned. A rejected claim frees the bounty again.

v0.39.8 — 2026-07-26

profile --delete no longer leaves files behind that nothing can ever read.

It promised to remove "your local terminalhire profile and encryption key". It removed the key, then left github-token.enc, chat-identity.enc, repo-experience.enc and claim-push-token.enc sitting on disk, encrypted under a key that no longer existed — and printed success.

  • Every encrypted file is now deleted with the key. The old code swept a list each store had to add itself to, and only one of the five ever did. The list is gone; --delete now clears every .enc file in the terminalhire directory.
  • If you already have orphaned files, run terminalhire profile --delete again. It works even when the key is already missing, so it clears what the older build left behind. No plaintext is touched — your config, saved claims, chat peers and session stay put.
  • A delete that cannot finish now stops instead of reporting success. If a file is locked (most likely on Windows, with another terminalhire running), the command exits with an error saying your key was not deleted, so nothing is orphaned. Close the other process and run it again.

v0.39.7 — 2026-07-26

A repo that welcomes AI with disclosure no longer scans as PROHIBITED.

Disclosure enforcement is not a ban (policy ruleset v3)

microsoft/TypeScript and microsoft/typescript-go both say they have no objection to AI assistance — you just have to disclose it. The consequence sentence ("if you do not include this disclosure, your PR will be closed") matched a prohibition pattern, so the policy scan reported the exact opposite of the repo's actual policy. The classifier now recognizes disclosure-enforcement sentences and reads them as what they are: the teeth of a disclosure rule, verdict disclosure-required. A genuine ban sharing a line with one still reads prohibited. Ruleset v2 → v3, so previously stored policy acknowledgments re-evaluate against the corrected verdict.

v0.39.6 — 2026-07-25

Founder-posted bounties reach you without --priced.

A founder posting is not scraped supply

Paid bounties sit behind --priced because scraped third-party paid listings used to crowd roles out of the default view. A founder posting is different: it is first-party, and someone is waiting to pay for that specific piece of work. Hiding it behind a flag inverts the point, so bountySource === 'founder' is now exempt from the demote. Scraped paid supply is unchanged.

Bounty lines also carry an effort estimate — small (~½ day), medium (~1 day), large (multi-day) — and a bounty you have already claimed says so, pointing at terminalhire claim status rather than offering to claim it again.

Why this is a separate release from v0.39.5

v0.39.5 was published minutes before the founder marketplace merged, so it does not contain these changes and npm versions cannot be rewritten. If you installed 0.39.5 you have every earlier fix; this release adds the founder supply path.

v0.39.5 — 2026-07-25

claim start tells you what it is doing.

Progress through fork, clone and branch

Claiming a large repository used to sit silent. On a repo the size of Firefox iOS that silence ran for minutes, which reads as a hang — there was no way to tell a working clone from a dead one. It now reports each phase as it goes:

  ✓ forking mozilla-mobile/firefox-ios → you/firefox-ios
  ✓ cloning you/firefox-ios receiving objects 62%
  ✓ cutting branch th/firefox-ios-4821

All three phases are covered, not just the clone. Forking is itself slow on a big repo, and the branch checkout has to write the whole worktree.

Progress goes to stderr, and only when both stderr and stdout are terminals. The jobs and bounties skills and the MCP server read this CLI's stdout and cannot tell decoration from data, so anything piping us gets output identical to before, byte for byte. Redirect stdout to a file and you lose the progress line — the cheaper mistake by a wide margin.

v0.39.4 — 2026-07-25

One change you can see, and it is a privacy one.

The spinner tip names the role, not the employer

A job tip used to read ↗ Senior Rust Engineer @ Stripe · 87% — …. It now reads ↗ Senior Rust Engineer · 87% — ….

That line is written into ~/.claude/settings.json, which Claude Code reads into every session — on any repo, on any machine, including a work laptop. So it is visible to anything that sees that file: a screen share, a screenshot, a support bundle, a colleague glancing over, a dotfiles repo you sync. The role on its own is ambient noise. The same line naming the employer says which company you are looking at, and for anyone looking quietly that is the whole sensitivity.

The link is unchanged, so the tip still clicks through to the same place. Bounty and contribute tips still show their repository — an open-source repo is public work, not an employer you are quietly eyeing.

Under the hood the company is still read on your machine, to keep two same-titled roles at different employers from collapsing into one another in the rotation. It is simply never written out.

v0.39.3 — 2026-07-25

Three robustness fixes. Most of this you will never see; two bits you will.

A corrupt key no longer dumps a stack trace at you

If the shared encryption key on disk was damaged, terminalhire profile --show printed a raw V8 stack trace and a Node.js v20 footer. It now prints the reason in one line and stops. The exit code is still non-zero, so scripts and CI see the failure exactly as before, and --verbose (or TERMINALHIRE_DEBUG=1) brings the full trace back when you actually want it.

spinner --show tells you when a background refresh skipped

The refresh loop can decline to write — someone else holds the lock, the settings file changed underneath it, a symlink will not resolve. It used to decline in silence, so a spinner that quietly stopped updating looked like nothing at all was happening. The last refusal is now recorded and shown, per surface, with the real reason rather than a catch-all.

Windows: the job-status lock stops fighting itself

On Windows the lock could fail with EPERM and crash the command. Two causes: the contended case was only recognised as EEXIST, and stale-lock recovery measured staleness against the waiting process's own clock — so it deleted locks that a healthy process was still holding, which on Windows leaves the file in a state where every later open fails. The recovery was manufacturing the error. Recovery now reads the lock file's own timestamp, and a lock that never frees is reported rather than ignored.

v0.39.2 — 2026-07-25

Name the project you want to work on.

terminalhire contribute <owner/repo>

contribute used to rank whatever the shared index already held. Now it takes a repo:

terminalhire contribute andrewyng/aisuite

That reads the repo's open, unassigned issues, ranks them against your local profile, and prints a claim command for each one.

If you have run terminalhire link, the read goes through terminalhire, which scans the repo's AI-assistance policy and shows the verdict at the top — and a repo whose CONTRIBUTING prohibits AI-assisted contributions gets no claim command on any row. Unlinked, the repo is read directly on your own GitHub token; there is no verdict to show yet, so the commands are printed and claim record scans the policy itself before recording anything. Either way the prohibition holds.

Bare terminalhire contribute behaves exactly as before.

Your profile does not leave the machine. Ranking is local, as always. The one new outbound call is a public read of the repo you named.

Naming a repo adds it to everyone's supply

When you are linked, a repo you name is submitted to the shared index, so the next person searching can find work there without knowing to ask for it. Submission changes what the crawler LOOKS at, never the bar it applies — a repo still has to clear the same quality gate to enter the shared pool, so a small project can be listed for you and never appear for anyone else. Submissions rotate round-robin and retire themselves after three crawls that surface nothing, so a few repos cannot crowd out the rest.

Looking up a repo without a linked session submits nothing.

v0.39.1 — 2026-07-25

A data-safety release. Two files the CLI writes to could be lost, and both failures were silent. Neither needed an attacker or an unusual setup.

The spinner will not overwrite a settings.json it cannot read

~/.claude/settings.json is not ours. Claude Code writes it, you hand-edit it, and it holds your permissions, hooks, MCP servers, model and environment. The spinner adds two keys to it, and used to do so by reading the file, editing what it read, and renaming a fresh copy over the top.

If the read failed, the edit started from an empty object. So a settings.json caught mid-save — an editor part-way through writing it — parsed as nothing, and the next refresh replaced your whole configuration with a file containing only spinner verbs. No warning, no backup. The refresh runs every ten minutes.

The writer now tells "there is no file" apart from "there is a file and I cannot read it", and refuses to write on the second. Related fixes went with it:

  • A symlinked settings.json is followed rather than replaced, so a dotfiles repo stays the live copy.
  • The file keeps its own permissions and is flushed to disk before the rename. It can be 0600 and hold secrets; every spinner write used to widen it.
  • A permission you approve while the spinner is mid-write is far less likely to be clobbered by the older copy.
  • A spinner block the CLI did not write is left alone instead of overwritten, and a deliberate spinnerTipsEnabled: false is restored by spinner --off rather than deleted.

spinner --on, --off, and the --mode/--max/--frequency flags now say when they skipped a write instead of reporting success either way.

One implementation of the chat key, so two processes cannot race it

crypto-store and github-auth each carried their own create-if-absent sequence for ~/.terminalhire/key. One had been hardened into a no-replace protocol; the other was still a plain check-then-write.

On a first run with no key yet, two processes could both see no key, both generate one, and both write — last one wins. Anything already encrypted under the loser's key became permanently unreadable, because that key then existed nowhere. claim push runs in the background and races foreground commands on a fresh install, so no unusual timing was required.

Key creation now lives in one place and both callers use it. A mangled key file also reports a clear error naming the file, instead of reading as empty and making every store look blank.

v0.39.0 — 2026-07-24

claim start speaks up when staying quiet would cost you the issue

Losing an issue you were already working on is usually not a technical failure. Someone else commented first, the maintainer assigned it to them, and your branch became dead work. Until now the CLI only spoke up when the repo's own docs asked it to — which left silent exactly the case where silence costs the most: a contested issue in a repo that states no process at all.

claim start now follows the same three-way rule the guide at terminalhire.com/social-layer teaches:

  • The repo states a process — follow it exactly, in their format. Unchanged.
  • No stated process, but someone else is already circling — post one substantive comment: your approach in a line, when a PR lands, and an explicit invitation for the maintainer to redirect you before you sink a day. Give it with --intent "..." (plus an optional --eta "..."), or answer the prompt.
  • No stated process, nobody else on it — say nothing. Performing a ritual nobody asked for burns goodwill before you have written a line.

You always see the exact comment before it exists, and nothing is posted without your yes. If you have no approach to offer, nothing is posted at all — a bare "can I work on this?" is what gets ignored, not what wins the issue.

Releasing a staked claim offers to hand the issue back

If you staked an issue publicly and then release the claim, claim release now offers to post a short stand-down. Without it, an issue you walked away from goes on reading as taken to everyone who looks — including our own tooling, for a fortnight.

It also will not quietly forget the stake. If nothing retracted it — you weren't at a terminal, or the post failed — the claim is kept rather than deleted, so the outstanding comment stays visible and retryable. --force releases anyway.

Also in this release

v0.38.0 was cut before a run of engine work landed, so the plugin and CLI pick all of it up here.

The AI-contribution policy scan got harder to fool. It decides whether we tell you a repo forbids AI-assisted contributions, so a miss is expensive in both directions — a false all-clear sends you into a repo that will reject the PR on sight, and a false alarm costs you a repo you were welcome in.

  • A keyword scan alone reads the letters, not the meaning. A second, semantic read now runs behind it, so a policy stated in prose rather than in the words we happened to grep for is no longer invisible.
  • An incomplete scan no longer resolves to a clean verdict. If we could not read everything, you are told that, rather than handed a "looks fine" that was never earned.
  • A spent anonymous rate limit is reported as a rate limit, not as a policy finding. Running out of GitHub reads is not evidence about the repo.

Fixes elsewhere in the CLI. First-run chat no longer races itself into a broken keystore; commands that hold secrets refuse a state directory that is a symlink; chat reads piped input without racing its own budget; and the plugin declares its own module type instead of inheriting one, which is what kept the refresh monitor working on Windows.

v0.38.0 — 2026-07-23

The ambient statusline can finally reach every role that matches you

v0.37.0 fixed the order roles came back in. It did not fix their reach, and that turned out to be the real cause of "I keep seeing the same job postings".

Slot budgeting kept the top 12 roles as a stable head and then rotated a window of the next 60 — so rank 72 was a ceiling nothing could cross. Out of 260 matching roles, 188 were structurally unreachable, and always the same 188. Four earlier rounds tuned rotation clocks inside that frozen window, which is why each helped a little and the complaint survived.

The tail is now uncapped and ordered by how long it has been since you last saw a role, with never-surfaced roles first. Measured over 200 surfaces against the same pool: 72 → 260 roles reachable, deepest rank 71 → 259, coverage 58% → 96%. The head is unchanged — top-fit roles stay visible by design.

TH_SEEN_SELECT=0 restores the previous behaviour.

The MCP tools (jobs, contribute) read the same cached array, so they inherit the wider reach for free.

Your seen-history no longer forgets while you are still looking

The store capped at 500 entries and the live store was sitting at exactly 500, so it had been quietly evicting for some time — and the selector cannot make a good decision about a role it has no memory of. Raised to 1500, and the probe now reports when the cap is reached instead of degrading invisibly.

A slow pipe into jpi is waited for, not truncated

The statusline hook read stdin under a flat 200 ms deadline measured from the first byte. A payload arriving in chunks — which is what a ~100 KB session blob through a 64 KB pipe does — could exhaust it mid-stream and render nothing.

The bound is now on inactivity, not total elapsed time: 200 ms while no data has arrived at all, 500 ms between chunks once a stream is in progress, and a 1.5 s hard ceiling. A stalled pipe still fails closed and fast; a slow-but-live writer is no longer cut off. Applies to both the POSIX and Windows readers.

v0.37.0 — 2026-07-22

Changed

  • terminalhire jobs no longer restarts at the top of the list every run. Roles you were shown recently sink and un-shown ones lead, so repeated runs work down your matches instead of replaying the same first page. Nothing is dropped — the count is still every role that matches you, just in a different order. Explicit paging (--page N, --all, --status) keeps the old fixed rank order so a page 2 still follows the page 1 you just read.
  • The jobs list is also more varied within a single page, using the same diversification the ambient statusline already applied. Previously the two surfaces ordered the same matches differently without saying so.
  • The MCP tools (jobs, bounties, contribute) stop returning the same list every call. They were re-sorting by match score and discarding the anti-repeat ordering the background refresh had already worked out, so an assistant asking twice in one session saw near-identical results. They now respect that ordering and cycle the lead every few minutes. Match scores are still reported on every result — they just no longer decide the sequence.

Added

  • terminalhire jobs --stable — opt out of the rotation and get the fixed, rank-ordered list when you want to look at exactly what you saw a minute ago.

Note

This release improves how listings are ordered on those surfaces. It does not change how many distinct roles the ambient statusline can reach, which is a separate limit still being worked on.

v0.36.1 — 2026-07-19

Changed

  • Maintenance: internal cleanup + build hardening. Removed dead code paths in the CLI's local-encryption layer (no change to how your data is stored or read) and added a build guard so the shipped plugin can never include a platform-specific native binary. No user-facing behavior changes.

v0.36.0 — 2026-07-19

Added

  • Reply to a connection without leaving your terminal. A new opt-in MCP chat server (terminalhire mcp-chat) lets you reply to an accepted Terminalhire connection through a native prompt right inside Claude Code. Your message thread stays on your machine — only the reply you type is sent. It's a separate, opt-in server from the read-only terminalhire mcp surface; enable it by adding it to your MCP host config.

Changed

  • Sharper "winnable" signal on contribution opportunities. Contribution matches now detect when an issue already has a cross-referenced pull request — including PR attempts that never left a comment — so the opportunities you see are less likely to be already taken.
  • More contribution opportunities surfaced. The contribution supply was widened, so a broader set of open-source issues can appear in your ambient surface.

v0.35.4 — 2026-07-18

Changed

  • Contributions are on by default. Open-source contribution opportunities — where a merged pull request is verifiable proof of work for your résumé — now appear in your ambient surface without a separate opt-in. They ride the same anonymous index you already download for jobs, so nothing new leaves your machine; this only changes what's shown. If you'd previously turned contributions off (or never enabled them), they'll now show — and the surface mix presets (config set mix jobs|balanced|credential, added in v0.35.3) finally control something visible. To opt out, set "contributeEnabled": false in ~/.terminalhire/config.json.
  • More varied lead cadence. Rotation clocks are de-phase-locked per install, so the roles surfaced in the spinner vary more between refreshes instead of advancing in lockstep.

Fixed

  • TERMINALHIRE_DIR is honored everywhere. If you point terminalhire at a custom state directory, every part of the CLI — config, cache, claims, and all other state — now respects it, instead of a few sites silently falling back to ~/.terminalhire.

v0.35.3 — 2026-07-18

Added

  • Surface mix presets — tune how much of the spinner is jobs vs. contributions. Run terminalhire config set mix jobs|balanced|credential to bias the ambient surface toward paid roles, an even split, or credential-building open-source contributions. The default now leans further toward contributions (balanced), and TH_MIX lets you override the preset per session without changing your saved config.

Changed

  • Shorter, cleaner job links. /j/ job URLs now use compact 8-character tokens, matching the /c/ contribution links — a Work-With-Remote link's token drops from 100+ characters to 8. Every job link you've already shared keeps working; only newly generated links use the short form.

v0.35.2 — 2026-07-17

Changed

  • The spinner's lead tip now rotates through your top-fit roles. Previously the most-visible first tip almost always showed the same #1 match; it now cycles over time across your top-tier roles, so the surface feels varied session to session while every lead stays a genuinely high-fit role. Set TH_LEAD_ROTATE=0 to restore the old always-best-first order.

v0.35.1 — 2026-07-17

Fixed

  • macOS claim deep links now open your terminal. The th:// / terminalhire:// claim-link handler previously delegated terminal-control to a detached background process that could not present the macOS Automation permission prompt, so a click could start and then do nothing. The handler now opens Terminal in the foreground of the link event (so macOS can grant the permission and attribute it to the Terminalhire handler), shows a notification with the exact recovery command if it still can't, and queues the claim so terminalhire claim/bounties/hub surface it on your next run. Existing installs self-heal on the next terminalhire command after updating, or run terminalhire protocol register.

v0.35.0 — 2026-07-17

  • terminalhire update (alias upgrade) — stop remembering the npm command. Running terminalhire update now checks your installed version against the latest published release (from your local index cache, no extra network call) and, if you're behind, auto-runs npm install -g terminalhire@latest for you — with a clear fallback message and the manual command if the install can't complete (e.g. permissions). --check reports installed vs. latest without installing anything; --force reinstalls even if you're already current. If you're running the Claude Code plugin's bundled copy, update tells you it updates with the plugin instead of touching your global npm install.

v0.34.0 — 2026-07-17

  • "Claim in terminal" links now work — run terminalhire protocol register once. A new terminalhire protocol register / status / unregister command registers the th:// URL scheme with your OS, so the Claim in terminal button on terminalhire.com opens the claim directly in your terminal instead of doing nothing. Registration is opt-in and one-time: init offers it as an optional Step 7, and installs that skip init (npm, homebrew, the plugin) never register it — so if that button has been silently failing for you, this release plus one terminalhire protocol register is the fix. protocol status tells you where you stand. The handler is local; a click passes only the claim token.
  • terminalhire repo <owner/repo> — a private memory of your history in a repo. New repo, repo note, and repo sync commands keep an encrypted, machine-local record of the repos you've worked in: how many PRs you've landed there, notes you write yourself, and a continuity signal built from your own merge history (repo sync backfills it from GitHub — the one explicit, user-initiated network call). Nothing here is uploaded, shared, or comparative across users.
  • bounties and contribute now favor repos you've already merged into. A repo you've landed work in gets a small local score boost and an inline note (↑ you've merged here twice), on the theory that your second PR into a codebase you know is likelier to land than your first into one you don't. On by default, TTY-only (piped output is byte-identical to before), and fully off with TERMINALHIRE_NO_CONTINUITY_RANK=1. The re-rank is computed locally from your own claim ledger.
  • claim submit now reports an acceptance forecast for the PR it just opened. A merge-probability estimate with its reasons (tests touched, files changed, diff size), computed locally from your diff and recorded on the claim so you can compare the forecast against what actually happened. It runs after the PR is opened and is advisory only — it never blocks or gates a submit, and any scorer error is swallowed rather than failing a submit that already pushed. Also fixes a real tracking bug: a PR that was closed and later reopened-and-merged is now correctly recorded as merged instead of being stuck at abandoned.
  • The acceptance credential no longer counts content-farm merges. A merged PR that just appends a numbered entry to a data file ("Add new Japanese Proverb 149") is not a coding merge, so it no longer inflates the credential computed at terminalhire login. The gate is deliberately narrow — only the distinctive numbered-seed farm signature drops, and real translation/i18n code work is kept — because a false drop silently erases proof of a merge you actually earned.
  • Hardening: everything under ~/.terminalhire is now written 0600/0700. Your claims ledger, encrypted profile key, and GitHub token are created with restrictive modes rather than whatever your umask happened to be, and re-login no longer leaves the token file at its old looser permissions.
  • Hardening: claims.json is safe under concurrent writers. The background refresh monitor and an interactive claim command can no longer interleave and corrupt the ledger; writes go through an on-disk mutex with unique temp names, and a stale lock self-heals after ~10s.
  • repo output now suppresses personalized continuity/calibration lines when piped, matching claim list and claim status.
  • Copy: the beta welcome no longer frames this as a résumé replacement ("your résumé tells the story; this backs it up"), and compute-location claims are stated as the guarantee we actually make — your profile never leaves your machine.

v0.33.0 — 2026-07-14

  • contribute and bounties now show what you've already claimed. An opportunity you hold an active local claim for is badged ● claimed by you in the list, and its call-to-action swaps from terminalhire claim <ref> to terminalhire claim status <id> — so a re-scan tells "I'm already on this" apart from "open," and points you at the right next command instead of the one that errors on a duplicate claim. The join is fully local (your claims ledger, matched by exact id) and the badge is distinct from the ⚠ N PRs in flight contention marker, which flags competition from other devs.

v0.32.0 — 2026-07-14

  • Repeat invocations now rotate what you see. The same top matches no longer pin themselves to every refresh:
    • devs directory: the top 5 stay stable, the rest of the list rotates through the full matched pool on a 5-minute window — and the directory now ranks the complete pool, fixing a bug where the list silently shrank to 9 entries.
    • Role matches: the previously permanent top-8 head now cycles hourly through the top-12 pool, so the head itself varies instead of only the tail. Ranking and privacy are unchanged — rotation reorders locally over the same locally-computed matches.
  • Test hardening: the refresh-monitor version-handover test no longer flakes on slow CI runners (it now accepts both valid shapes of the legacy monitor's termination).

v0.31.1 — 2026-07-13

  • Hardening: policy-audit excerpts are sanitized before display. The verbatim CONTRIBUTING.md/AGENTS.md excerpts shown at claim record/preview are attacker-controlled text (the target repo writes them). Control characters other than newline and tab are now stripped at excerpt creation, so an embedded ANSI or OSC escape sequence can't clear your screen, write your clipboard, or otherwise act on the terminal from the exact code path meant to make you read a repo's policy. Classification is unaffected. (Flagged in post-merge review of v0.31.0.)

v0.31.0 — 2026-07-13

  • The claim-time policy scan now classifies instead of just flagging. claim record/preview audit the repo's CONTRIBUTING.md, AGENTS.md, and PR template and return a verdict — prohibited (repo bans AI-generated contributions), disclosure-required, ai-mentioned, or clean — plus the contribution requirements they state (assignment expected, /take bot, CLA, discussion-first), each with the verbatim excerpt so you read the repo's actual words, not our paraphrase.
  • A prohibited verdict gets a dedicated gate. The generic --ack-policy no longer clears it; recording the claim takes an interactive confirm (or the explicit --ack-policy-prohibited flag) that spells out that everything you submit must be hand-written. The /claim skill is required to put that decision to the human — it can't self-attest past a ban.
  • The assignment request at claim start is now audit-driven and previewed. The comment is only posted when the repo's docs actually expect it (/take on bot repos, a plain request otherwise — including bots detected by the audit, not just the built-in list); repos with no stated expectation get no comment (pass --assign to request anyway). On an interactive terminal you see the exact comment body and confirm before it posts, and the copy has been de-botified. Claims recorded before this release keep the old always-post behavior.
  • Prompted by a real incident: a claim on a repo that prohibits AI-generated contributions auto-posted a public assignment comment. This release makes that combination impossible without a deliberate human yes.

v0.30.2 — 2026-07-13

  • Internal cleanup: dropped the local click-tracking hop behind job tip links. Now that every statusline tip link points straight at terminalhire.com (v0.30.1), the loopback server the background monitor used to record a click locally before redirecting was dead weight — it's been removed end to end. Nothing changes in what you see or how clicks are counted (the public link still logs the click server-side); it's just one fewer moving part running in the background.

v0.30.1 — 2026-07-13

  • Job tip links always show terminalhire.com. The role and bounty links in the statusline now consistently point at terminalhire.com/j/… instead of occasionally surfacing a bare 127.0.0.1 address that looked broken. Clicking tracks exactly as before — the public link records the click server-side.

v0.30.0 — 2026-07-13

  • claim audit <id> — a read-only look at a claim's PR lifecycle. A new command that walks a contribution claim's pull-request history — review rounds, state changes, merge outcome — and prints a stateless audit pulled fresh from GitHub. Nothing is scored and nothing is stored; it's the first step of the contributor- reputation work.

v0.29.0 — 2026-07-13

  • A nudge when your dashboard's claim count is behind. If you push your claims to the terminalhire dashboard manually, the CLI now tells you — in claim list and ambiently in the statusline — when your local claims have moved on since your last push, so your public "merged" count doesn't silently lag reality. It points you at claim --push --keep-updated, which keeps the dashboard in sync automatically from then on. Devs already on auto-sync never see it, and it reads only local files — nothing new leaves your machine.

v0.28.0 — 2026-07-11

  • Claiming asks for the assignment, automatically. claim start requests assignment on the issue as part of provisioning — /take on repos with a self-assign bot, a plain-English request everywhere else — so you follow the repo's "get assigned before you open a PR" etiquette without having to remember it. It's idempotent (won't double-post if you're already assigned or already asked) and best-effort (an unauthenticated gh or a network hiccup warns, never blocks your work).
  • claim submit blocks a duplicate PR before you open one. If someone else already has an open pull request against the same issue, submit now stops with an explanation instead of letting you ship a PR a maintainer will just close — the hard lesson from a race lost mid-build. Your own fork's PR never trips it, and an uncertain or degraded read never blocks (it stops only on a confirmed competing PR). --force-competing is there if you've decided to proceed anyway.

v0.27.0 — 2026-07-11

  • One command to start a claim: terminalhire claim start. After you claim an issue, claim start now provisions the whole workspace end to end — fork, clone, branch, and attach — in a single step, replacing the old attach --worktree … --branch … ceremony. It clones into a managed directory and simply prints the cd for you; it never touches your current shell, cwd, or branch. Run it bare to pick from your claimed issues, or --here when you're already in the target repo. submit now self-advances from the working state, so the interim update <id> ready step is optional.

v0.26.2 — 2026-07-10

  • Clearer error when you tack an extra word onto a claim shortref. Running claim <shortref> with a stray trailing argument (e.g. a pasted issue title) no longer bails with a generic message — it now points you at the exact command it thinks you meant, so you can fix the typo without re-reading the help.

v0.26.1 — 2026-07-10

  • claim <shortref> just works. Typing a claim shortref with no verb in front of it used to be rejected as an unknown command; it now routes straight to claim record, matching how the rest of the CLI treats a bare identifier.

v0.26.0 — 2026-07-08

  • Jobs you've already opened stop coming back. When you click a job link from the statusline tip (or anywhere the CLI surfaces one), that click is now recorded locally, so the same listing doesn't keep resurfacing on your next refresh. The jobs you've engaged with move out of the "new" surface and stay reachable in your Viewed history — nothing is lost, it just stops nagging you. (A click isn't an application, so it's tracked as Viewed, not Applied — the distinction is preserved on purpose.)
  • Still 100% local — that's the point. Which jobs you clicked is recorded on your machine only. The click still redirects through terminalhire.com/j/… so the anonymous click analytics we already collect are unchanged, but which job it was never leaves your machine — there's no server-side record of your job history to leak, subpoena, or breach. The background refresh monitor listens on 127.0.0.1 only; no new network access.

v0.25.0 — 2026-07-07

  • Claimable work is now ranked by how winnable it is, not how it's priced. bounties and the hub's Bounties pane surface open-source issues ordered by a winnability signal — an active maintainer who actually merges outside pull requests, low contention, recent activity, and a match to your local vocab — so the top of the list is the work you're most likely to get merged, not just the loudest listing. A repo with big stars but a history of never merging external PRs no longer floats to the top. (Ranking is computed server-side and shipped in the index; the CLI still ranks locally with zero egress.)
  • Paid public bounties are paused by default. Every paid public bounty we surfaced turned out to be spammed-out, already-done, or not real, so they're hidden from the default surface for now — nothing is deleted and the claim flow is unchanged. Run bounties --priced to see them, or reach for terminalhire contribute for merge-ready open-source work. We'll bring paid bounties back when we have more say in their quality.
  • Repo discovery for the contribute feed. The contribute feed now seeds high-traction candidate repos (trending ∪ vocab-adjacent) and surfaces their open good-first-issue / help-wanted issues, scored by the same winnability pipeline — widening supply beyond what you've already touched.
  • Maintainer/Founder credential (web). If you started a project that enough outside contributors build on, your resume card and dashboard now show a reach-only "Maintainer" credential. (Surfaces on the web dashboard; the bundled matching engine carries the supporting signals.)

v0.24.1 — 2026-07-06

  • Hub mouse support. In the full-screen terminalhire hub TUI you can now click a nav-rail section to select it, or click a Jobs/Bounties row to open it. The hub stays keyboard-native; mouse is purely additive. (Mouse capture disables native terminal text-selection unless you hold Shift, or Option on macOS Terminal/iTerm.)

v0.24.0 — 2026-07-06

  • Better matching for what you're aiming at. Your stated project aspiration now persists across runs and feeds a three-tier vocabulary graph, so matches track the work you want to be doing, not just what you've done.

v0.23.0 — 2026-07-06

  • terminalhire hub — a full-screen, keyboard-native TUI. One running app that unifies jobs, bounties, connect, the chat inbox, claims, and your profile behind a nav rail and command palette. Every existing terminalhire <command> keeps working exactly as before — the hub is additive, zero new dependencies, and still 100% local.

v0.22.0 — 2026-07-06

  • Frictionless claim submit + contention watch. Submitting a claimed bounty PR is smoother, and you can now see when others are racing the same issue — comment fast, win the merge.

v0.21.0 — 2026-07-05

  • Passive interest capture at init — the matcher learns what you're drawn to without any extra input, and adds a disjoint "Stretch" job slot.
  • Claim-sync robustness improvements.

v0.20.0 — 2026-07-05

  • Interactive update prompt, a minimum-value bounty floor, a scope-C "Stretch" slot, and a dashboard claim view (with opt-in periodic background push).

v0.19.x — 2026-07-05

  • v0.19.2 — Beta daily pulse: a light 0–3 question micro check-in.
  • v0.19.1 — Feedback quick-form + a listener fix.
  • v0.19.0 — Surfacing upgrades (soft tags + MMR re-ranking for variety), a contribute-quality classifier, claim hardening, and the beta program.

v0.18.x — 2026-07-03 → 07-04

  • v0.18.2 — Spinner engagement-suppression (quieter when you're heads-down).
  • v0.18.1 — Feed-text sanitizer + unified bounty effort thresholds.
  • v0.18.0 — Thin-profile contribute on-ramp — start contributing before your profile is fully built out.

v0.17.x — 2026-07-03

  • v0.17.2 — Session-stale link fix + clearer CLI labels.
  • v0.17.1 — Apex-domain hotfix.
  • v0.17.0 — Harness expansion: the terminalhire MCP server (use it from any AI editor) plus a VS Code / Cursor / Windsurf status-bar extension.

v0.16.x — 2026-07-02

  • v0.16.1 — Inbox UX polish.
  • v0.16.0 — Interactive inbox TUI.

v0.15.0 — 2026-07-02

  • Hardening train — stability and correctness across the CLI.

v0.14.0 — 2026-07-01

  • Spinner content freshness — anti-repeat and wider variety in the ambient surface.

v0.13.0 — 2026-07-01

  • Contribute — a feed of credential-building open-source issues — plus the recruiter-legible credential projection.

v0.12.0 — 2026-07-01

  • Connection notifications in the Claude Code status line.

v0.11.x — 2026-07-01

  • v0.11.1 — Chat presence-integrity fix.
  • v0.11.0 — Presence → reachability refinements.

v0.10.x — 2026-06-30 → 07-01

The connections + chat train:

  • v0.10.11 — Presence becomes "reachability" + consented activity + a session self-diagnostic.
  • v0.10.10 — Pending invitations in the chat inbox + an inline picker.
  • v0.10.9 — Badge correctness (single monitor, read-clears-badge).
  • v0.10.8 — Fix connection badges silently dying (apex→www cookie strip) + a loud session-expiry hint.
  • v0.10.7 — Chat uses the alternate screen buffer (no more frame stacking).
  • v0.10.6 — Connection badges always show (no longer nudge-gated).
  • v0.10.5 — Unread-messages badge in the status line.
  • v0.10.4 — Chat-first contact (lead with chat; handle as the default).
  • v0.10.3 — Intros work with just a GitHub username (no email required).
  • v0.10.2 — Connect follow-ups: devs self-filter, sender view, disclosure.
  • v0.10.1 — Frictionless intro accept.
  • v0.10.0 — Connect-loop hardening: inbound nudge, dashboard, honesty + staleness.

v0.9.x — 2026-06-30

  • v0.9.2 — Fixes.
  • v0.9.1terminalhire link — bridge a CLI session to the web.
  • v0.9.0 — 1:1 end-to-end encrypted connections chat + an inline reader.

v0.8.0 — 2026-06-30

  • Ambient peer / founder surfacing, behind an opt-in gate.

v0.7.x — 2026-06-30

  • v0.7.1 — Fixes.
  • v0.7.0 — Two-sided match + consented connection (the intro flow).

v0.6.0 — 2026-06-29

  • terminalhire claim submit + attach — claim a bounty and attach your PR.

v0.5.0 — 2026-06-27

  • terminalhire trajectory (with MCP-name redaction).

v0.4.x — 2026-06-16 → 06-27

  • v0.4.6 — Sign into the website automatically after CLI GitHub login.
  • v0.4.5 — Styled, confirm-before-claim preview.
  • v0.4.4 — Installer pin.
  • v0.4.1 — Spinner leads with roles, bounties secondary.
  • v0.4.0Proof-of-Work acceptance credential — your merged PRs as a verified competence signal.

v0.3.x — 2026-06-15

  • v0.3.5 — Neutral spinner verbs.
  • v0.3.4 — Rotate + alternate bounties so several show, not one.
  • v0.3.3 — Bounties lead the verb + tip.
  • v0.3.2 — Guarantee bounty slots.
  • v0.3.1 — Bounty-aware ambient surface.
  • v0.3.0terminalhire bounties.

v0.1.0 – v0.2.5 — 2026-06-14 → 06-15

  • Initial releases: the local-first job matcher and ambient Claude Code spinner surface. Started life as the "Hireline" MVP, rebranded to terminalhire with GitHub OAuth, auto-learning, and hybrid matching. Your profile has never left your machine — from day one.