From 7f9edf54c1f9af7bc89c4ea58eba5924a8e770bf Mon Sep 17 00:00:00 2001 From: Mads Lorentzen <50207393+MadsLorentzen@users.noreply.github.com> Date: Mon, 26 Oct 2026 14:19:53 +0100 Subject: [PATCH] chore: point every in-tree reference at ai-job-search-dev/ai-job-search (#776) The repository is moving from the MadsLorentzen personal account to the ai-job-search-dev organization. GitHub redirects the old slug, but the upstream-watch guard compares github.repository against the literal slug and would start running the fork-only triage job on upstream itself, and the upstream-check tool and its tests pin the slug too. README, CONTRIBUTING, SECURITY, SETUP, the CI workflow, the issue-template config and the CHANGELOG links follow. Co-authored-by: Claude Fable 5.1 --- .../skills/freehire-search/cli/src/helpers.ts | 2 +- .../skills/freehire-search/url-reference.md | 2 +- .../cli/tests/fixtures/README.md | 2 +- .../tests/fixtures/search-cards.provenance.md | 2 +- .github/ISSUE_TEMPLATE/config.yml | 2 +- .github/workflows/ci.yml | 6 +- .github/workflows/upstream-watch.yml | 4 +- CHANGELOG.md | 44 ++++++++----- CONTRIBUTING.md | 66 +++++++++---------- README.md | 14 ++-- SECURITY.md | 4 +- SETUP.md | 6 +- tests/_upstream.py | 2 +- tests/test_check_upstream_updates.py | 2 +- tests/test_tools_utf8_output.py | 2 +- tests/test_upstream_checkout.py | 16 ++--- tests/test_upstream_triage.py | 2 +- tools/check_upstream_updates.py | 2 +- 18 files changed, 94 insertions(+), 86 deletions(-) diff --git a/.agents/skills/freehire-search/cli/src/helpers.ts b/.agents/skills/freehire-search/cli/src/helpers.ts index 83ad3718..943b208c 100644 --- a/.agents/skills/freehire-search/cli/src/helpers.ts +++ b/.agents/skills/freehire-search/cli/src/helpers.ts @@ -23,7 +23,7 @@ export function writeError(error: string, code: string): void { // cli/src, so a github.com URL here made every github.com link classify as an // installed job board instead of an unverified host. The owner/project name // already identifies the repo on GitHub. -const UA = "MadsLorentzen/ai-job-search/1.0 (freehire-search)" +const UA = "ai-job-search-dev/ai-job-search/1.0 (freehire-search)" /** The shared API response envelope: {data, meta, error}. */ export interface Envelope { diff --git a/.agents/skills/freehire-search/url-reference.md b/.agents/skills/freehire-search/url-reference.md index d05e71e7..6f1b0cb4 100644 --- a/.agents/skills/freehire-search/url-reference.md +++ b/.agents/skills/freehire-search/url-reference.md @@ -138,7 +138,7 @@ points users to (`?q=` scopes the counts). Example: serves HTML, which `cleanHtml` (`cli/src/helpers.ts`) strips into readable text. Search descriptions arrive already rendered by the API and are passed through verbatim — stripping them again would undo the Markdown structure. -- Fetch sends `User-Agent: MadsLorentzen/ai-job-search/1.0 (freehire-search)` (no contact +- Fetch sends `User-Agent: ai-job-search-dev/ai-job-search/1.0 (freehire-search)` (no contact URL: a host literal in `cli/src` makes `/apply` treat that host as this portal's board) and `Accept: application/json`, and uses exponential backoff with jitter on 429/5xx (max 6 retries). A connection error (API unreachable) fails fast with a clear message — no retry, since it is not diff --git a/.agents/skills/linkedin-search/cli/tests/fixtures/README.md b/.agents/skills/linkedin-search/cli/tests/fixtures/README.md index bb9d1adc..d4c943b3 100644 --- a/.agents/skills/linkedin-search/cli/tests/fixtures/README.md +++ b/.agents/skills/linkedin-search/cli/tests/fixtures/README.md @@ -2,7 +2,7 @@ `detail-orbis-excerpt.html` contains verbatim excerpts from the guest response for posting `4345841651`, captured by **portapulito** through this CLI's `htmlFetch` -on 2026-07-14 and published in [upstream PR #153](https://github.com/MadsLorentzen/ai-job-search/pull/153). +on 2026-07-14 and published in [upstream PR #153](https://github.com/ai-job-search-dev/ai-job-search/pull/153). That PR was closed because it was accidentally submitted to upstream instead of the author's personalized fork; it was not merged. diff --git a/.agents/skills/linkedin-search/cli/tests/fixtures/search-cards.provenance.md b/.agents/skills/linkedin-search/cli/tests/fixtures/search-cards.provenance.md index a9e4345a..51d4648e 100644 --- a/.agents/skills/linkedin-search/cli/tests/fixtures/search-cards.provenance.md +++ b/.agents/skills/linkedin-search/cli/tests/fixtures/search-cards.provenance.md @@ -2,7 +2,7 @@ `search-cards.json` stores the HTML string for the first two verbatim `
  • ` cards from a real LinkedIn guest search response captured by **portapulito** through the CLI's -`htmlFetch` on 2026-07-14 and published in [PR #153](https://github.com/MadsLorentzen/ai-job-search/pull/153). +`htmlFetch` on 2026-07-14 and published in [PR #153](https://github.com/ai-job-search-dev/ai-job-search/pull/153). That PR was closed as an accidental personalized-fork submission, not merged. Source: `.agents/skills/linkedin-search/cli/test/__fixtures__/search-list.html` diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 3d12f043..2a8688d2 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,7 +1,7 @@ blank_issues_enabled: true contact_links: - name: Filing from a personalized fork? Read this first - url: https://github.com/MadsLorentzen/ai-job-search/blob/master/SETUP.md#2-fork-and-clone + url: https://github.com/ai-job-search-dev/ai-job-search/blob/master/SETUP.md#2-fork-and-clone about: >- The gh CLI in a fork clone targets THIS public repo by default. Personal application tracking, evaluations, and incident logs belong in your own diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 120bc95a..38d3100a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: - run: pip install pyyaml - run: python tools/lint_skills.py - name: Framework version guard (upstream template only) - if: github.repository == 'MadsLorentzen/ai-job-search' + if: github.repository == 'ai-job-search-dev/ai-job-search' run: python tools/check_framework_version.py security-guards: @@ -203,7 +203,7 @@ jobs: python3 tools/verify_pdf.py cv/main_example.pdf --min-chars 100 python3 tools/verify_pdf.py cover_letters/cover_example.pdf --min-chars 100 - name: Assert stock PDF structure (upstream template only) - if: github.repository == 'MadsLorentzen/ai-job-search' + if: github.repository == 'ai-job-search-dev/ai-job-search' run: | python3 tools/verify_pdf.py cv/main_example.pdf \ --pages 2 \ @@ -269,7 +269,7 @@ jobs: placeholder-integrity: name: Placeholder integrity (upstream template only) - if: github.repository == 'MadsLorentzen/ai-job-search' + if: github.repository == 'ai-job-search-dev/ai-job-search' runs-on: ubuntu-latest steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 diff --git a/.github/workflows/upstream-watch.yml b/.github/workflows/upstream-watch.yml index e63a4cfd..4fae1ebb 100644 --- a/.github/workflows/upstream-watch.yml +++ b/.github/workflows/upstream-watch.yml @@ -38,7 +38,7 @@ jobs: name: Triage upstream commits # No-op on the upstream template itself. Pinned by # tests/test_upstream_triage.py so a template clone never runs it by surprise. - if: github.repository != 'MadsLorentzen/ai-job-search' + if: github.repository != 'ai-job-search-dev/ai-job-search' runs-on: ubuntu-latest # Issue write is granted to this one job, not the workflow, so a job added # later starts read-only. @@ -57,7 +57,7 @@ jobs: - name: Fetch upstream template run: | - git remote add upstream https://github.com/MadsLorentzen/ai-job-search.git 2>/dev/null || true + git remote add upstream https://github.com/ai-job-search-dev/ai-job-search.git 2>/dev/null || true git fetch --quiet upstream master - name: Build triage report diff --git a/CHANGELOG.md b/CHANGELOG.md index 15936051..79104a6c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,14 @@ per-file diff commands. ### Changed +- **The repository moved to the `ai-job-search-dev` organization** - the canonical URL is now + `https://github.com/ai-job-search-dev/ai-job-search`. GitHub redirects the old + `MadsLorentzen/ai-job-search` address for web links, `git` remotes, raw files and the API, so + existing clones and forks keep working; update your `upstream` remote when convenient: + `git remote set-url upstream https://github.com/ai-job-search-dev/ai-job-search.git`. Every + in-tree reference (README, CONTRIBUTING, SECURITY, the upstream-check tool and its tests, the + workflows' upstream guard) now points at the new slug. + - **`freehire-search` documents the `OR` and `title:` query operators** (#674, `.agents/skills/freehire-search/SKILL.md`, `url-reference.md`, `cli/README.md`, `--help`) - freehire's search parses an uppercase `OR` between up to five alternatives and a leading `title:` @@ -272,7 +280,7 @@ per-file diff commands. `github.com` among them, so a "posting" published in any GitHub repo or gist classified as an installed portal instead of getting the `⚠ Unverified source host` warning. The User-Agent keeps the `owner/project/version` name freehire's API docs ask for and drops the optional URL: - `MadsLorentzen/ai-job-search/1.0 (freehire-search)`. The host-check test now lists `github.com` and + `ai-job-search-dev/ai-job-search/1.0 (freehire-search)`. The host-check test now lists `github.com` and `gist.github.com` postings among the hosts that must stay unverified. - **The `/reset` replay tests find a Bash that actually runs the script on Windows** (#671, @@ -528,7 +536,7 @@ per-file diff commands. `.agents/skills/freehire-search/cli/src/helpers.ts`, `url-reference.md`) - the old `freehire-search-skill/1.0 (+https://freehire.me)` pointed at the API itself, so freehire's operators could not tell whose client it was. It now sends - `MadsLorentzen/ai-job-search/1.0 (+https://github.com/MadsLorentzen/ai-job-search; freehire-search)`, + `ai-job-search-dev/ai-job-search/1.0 (+https://github.com/ai-job-search-dev/ai-job-search; freehire-search)`, the `owner/project` form freehire's API docs ask for. Nothing behaves differently; the API does not gate on it. @@ -2539,7 +2547,7 @@ per-file diff commands. Step 5 and then discarded. Both arrays are now stored verbatim and replaced (never accumulated) on `--all` re-ranks, so downstream consumers of `seen_jobs.json` can read real triage findings instead of re-deriving them. See - [discussion #258](https://github.com/MadsLorentzen/ai-job-search/discussions/258). + [discussion #258](https://github.com/ai-job-search-dev/ai-job-search/discussions/258). - **`/upskill` aggregate mode now ingests `/rank`'s recorded gaps** - previously it only read `job_search_tracker.csv` and *guessed* required skills from the `role`/`sector`/ `notes` columns, even though `/rank` had already fetched and scored postings that never @@ -2549,7 +2557,7 @@ per-file diff commands. skill list wherever both exist. The heatmap's Gap Source column now shows the recorded-vs-inferred split per skill, and the report header states how many jobs came from each source. Depends on #263 (`/rank` persisting `gaps`/`strengths`); see - [discussion #258](https://github.com/MadsLorentzen/ai-job-search/discussions/258). + [discussion #258](https://github.com/ai-job-search-dev/ai-job-search/discussions/258). ### Security & privacy @@ -2559,7 +2567,7 @@ per-file diff commands. documents the safe alternative (a private repository with this repo as `upstream`), and `/setup` ends with a matching privacy note the moment profile data first lands in tracked files. Prompted by - [discussion #266](https://github.com/MadsLorentzen/ai-job-search/discussions/266). + [discussion #266](https://github.com/ai-job-search-dev/ai-job-search/discussions/266). - **The gitignore guard now covers two more personal-data rules** - `security_guards.py` requires `cover_letters/Cover_*.*` (the uppercase cover-letter naming variant `/apply` recognizes) and `cv/*.txt` (ATS text extractions of tailored CVs) in `.gitignore`, so a @@ -2586,7 +2594,7 @@ per-file diff commands. - CONTRIBUTING's "run what CI runs" list is now complete - it previously omitted `tools/security_guards.py` and the exact `unittest` invocation, the precise checks a contributor PR had already failed on. Prompted by - [issue #262](https://github.com/MadsLorentzen/ai-job-search/issues/262). + [issue #262](https://github.com/ai-job-search-dev/ai-job-search/issues/262). ## [1.1.0] - 2026-07-30 @@ -2677,15 +2685,15 @@ At this baseline the framework provides: - **Cross-runtime support** - a root `AGENTS.md` pointer so Codex and Antigravity can discover the portable portal skills, with Claude Code as the reference runtime. -[Unreleased]: https://github.com/MadsLorentzen/ai-job-search/compare/v1.7.3...HEAD -[1.7.3]: https://github.com/MadsLorentzen/ai-job-search/compare/v1.7.2...v1.7.3 -[1.7.2]: https://github.com/MadsLorentzen/ai-job-search/compare/v1.7.1...v1.7.2 -[1.7.1]: https://github.com/MadsLorentzen/ai-job-search/compare/v1.7.0...v1.7.1 -[1.7.0]: https://github.com/MadsLorentzen/ai-job-search/compare/v1.6.0...v1.7.0 -[1.6.0]: https://github.com/MadsLorentzen/ai-job-search/compare/v1.5.0...v1.6.0 -[1.5.0]: https://github.com/MadsLorentzen/ai-job-search/compare/v1.4.0...v1.5.0 -[1.4.0]: https://github.com/MadsLorentzen/ai-job-search/compare/v1.3.0...v1.4.0 -[1.3.0]: https://github.com/MadsLorentzen/ai-job-search/compare/v1.2.0...v1.3.0 -[1.2.0]: https://github.com/MadsLorentzen/ai-job-search/compare/v1.1.0...v1.2.0 -[1.1.0]: https://github.com/MadsLorentzen/ai-job-search/compare/v1.0.0...v1.1.0 -[1.0.0]: https://github.com/MadsLorentzen/ai-job-search/releases/tag/v1.0.0 +[Unreleased]: https://github.com/ai-job-search-dev/ai-job-search/compare/v1.7.3...HEAD +[1.7.3]: https://github.com/ai-job-search-dev/ai-job-search/compare/v1.7.2...v1.7.3 +[1.7.2]: https://github.com/ai-job-search-dev/ai-job-search/compare/v1.7.1...v1.7.2 +[1.7.1]: https://github.com/ai-job-search-dev/ai-job-search/compare/v1.7.0...v1.7.1 +[1.7.0]: https://github.com/ai-job-search-dev/ai-job-search/compare/v1.6.0...v1.7.0 +[1.6.0]: https://github.com/ai-job-search-dev/ai-job-search/compare/v1.5.0...v1.6.0 +[1.5.0]: https://github.com/ai-job-search-dev/ai-job-search/compare/v1.4.0...v1.5.0 +[1.4.0]: https://github.com/ai-job-search-dev/ai-job-search/compare/v1.3.0...v1.4.0 +[1.3.0]: https://github.com/ai-job-search-dev/ai-job-search/compare/v1.2.0...v1.3.0 +[1.2.0]: https://github.com/ai-job-search-dev/ai-job-search/compare/v1.1.0...v1.2.0 +[1.1.0]: https://github.com/ai-job-search-dev/ai-job-search/compare/v1.0.0...v1.1.0 +[1.0.0]: https://github.com/ai-job-search-dev/ai-job-search/releases/tag/v1.0.0 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6773ab85..f1b306a2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -43,7 +43,7 @@ Reviews here are empirical. Bug reports are reproduced on master before the fix ## Building for your own market? Do this instead 1. Fork the repo and run `/add-portal` with your local job board - it scaffolds a portal skill matching the shipped contract, and `/scrape` picks it up automatically. -2. Announce your fork in the pinned [Community forks & adaptations](https://github.com/MadsLorentzen/ai-job-search/discussions/78) discussion so others can find it. +2. Announce your fork in the pinned [Community forks & adaptations](https://github.com/ai-job-search-dev/ai-job-search/discussions/78) discussion so others can find it. 3. Run the framework update checker (`python3 tools/check_upstream_updates.py`) in your fork to check if upstream has updated any framework files and compare them with your personalized variants. Market-specific skills are genuinely valuable - they just live in forks, where their maintainers can test them and their users can find them. @@ -60,9 +60,9 @@ What upstream maintains for other runtimes instead: - The root `AGENTS.md` points any agent at the canonical workflow specs and the profile entry point. - Framework instruction files carry `framework_version` markers, so a runtime fork can track methodology changes precisely (`python3 tools/check_upstream_updates.py`). -Announce your runtime fork in the pinned [Community forks & adaptations](https://github.com/MadsLorentzen/ai-job-search/discussions/78) discussion and it gets listed alongside the market adaptations. The proven shape is a thin pointer: reference the specs here instead of copying them, so upstream improvements reach your fork on rebase. +Announce your runtime fork in the pinned [Community forks & adaptations](https://github.com/ai-job-search-dev/ai-job-search/discussions/78) discussion and it gets listed alongside the market adaptations. The proven shape is a thin pointer: reference the specs here instead of copying them, so upstream improvements reach your fork on rebase. -This is a decision, not a dogma: if cross-runtime standards mature to the point where these specs run unmodified elsewhere, or the community's center of gravity moves to runtime forks, the trade-off gets re-evaluated. Background: the architecture thread in [Community forks & adaptations](https://github.com/MadsLorentzen/ai-job-search/discussions/78). +This is a decision, not a dogma: if cross-runtime standards mature to the point where these specs run unmodified elsewhere, or the community's center of gravity moves to runtime forks, the trade-off gets re-evaluated. Background: the architecture thread in [Community forks & adaptations](https://github.com/ai-job-search-dev/ai-job-search/discussions/78). ## Practical notes @@ -70,34 +70,34 @@ This is a decision, not a dogma: if cross-runtime standards mature to the point - **Personal-use boundaries**: portal skills that touch ToS-restricted sources carry a prominent personal-use-only warning, and CI deliberately makes no live portal requests. Don't "fix" that. - **LaTeX changes**: both templates must compile (`lualatex` for the CV, `xelatex` for the cover letter) and hold their exact page counts. CI smoke-checks this. -Questions and proposals are welcome in [Discussions](https://github.com/MadsLorentzen/ai-job-search/discussions) - an Idea thread costs nothing and can save you building the wrong thing :-) +Questions and proposals are welcome in [Discussions](https://github.com/ai-job-search-dev/ai-job-search/discussions) - an Idea thread costs nothing and can save you building the wrong thing :-) -[#17]: https://github.com/MadsLorentzen/ai-job-search/issues/17 -[#30]: https://github.com/MadsLorentzen/ai-job-search/issues/30 -[#31]: https://github.com/MadsLorentzen/ai-job-search/issues/31 -[#35]: https://github.com/MadsLorentzen/ai-job-search/issues/35 -[#36]: https://github.com/MadsLorentzen/ai-job-search/issues/36 -[#37]: https://github.com/MadsLorentzen/ai-job-search/issues/37 -[#39]: https://github.com/MadsLorentzen/ai-job-search/issues/39 -[#41]: https://github.com/MadsLorentzen/ai-job-search/issues/41 -[#43]: https://github.com/MadsLorentzen/ai-job-search/issues/43 -[#44]: https://github.com/MadsLorentzen/ai-job-search/issues/44 -[#49]: https://github.com/MadsLorentzen/ai-job-search/issues/49 -[#52]: https://github.com/MadsLorentzen/ai-job-search/issues/52 -[#54]: https://github.com/MadsLorentzen/ai-job-search/issues/54 -[#55]: https://github.com/MadsLorentzen/ai-job-search/issues/55 -[#56]: https://github.com/MadsLorentzen/ai-job-search/issues/56 -[#59]: https://github.com/MadsLorentzen/ai-job-search/issues/59 -[#60]: https://github.com/MadsLorentzen/ai-job-search/issues/60 -[#63]: https://github.com/MadsLorentzen/ai-job-search/issues/63 -[#64]: https://github.com/MadsLorentzen/ai-job-search/issues/64 -[#66]: https://github.com/MadsLorentzen/ai-job-search/issues/66 -[#67]: https://github.com/MadsLorentzen/ai-job-search/issues/67 -[#68]: https://github.com/MadsLorentzen/ai-job-search/issues/68 -[#72]: https://github.com/MadsLorentzen/ai-job-search/issues/72 -[#73]: https://github.com/MadsLorentzen/ai-job-search/issues/73 -[#75]: https://github.com/MadsLorentzen/ai-job-search/issues/75 -[#76]: https://github.com/MadsLorentzen/ai-job-search/issues/76 -[#155]: https://github.com/MadsLorentzen/ai-job-search/pull/155 -[#162]: https://github.com/MadsLorentzen/ai-job-search/pull/162 -[#165]: https://github.com/MadsLorentzen/ai-job-search/pull/165 +[#17]: https://github.com/ai-job-search-dev/ai-job-search/issues/17 +[#30]: https://github.com/ai-job-search-dev/ai-job-search/issues/30 +[#31]: https://github.com/ai-job-search-dev/ai-job-search/issues/31 +[#35]: https://github.com/ai-job-search-dev/ai-job-search/issues/35 +[#36]: https://github.com/ai-job-search-dev/ai-job-search/issues/36 +[#37]: https://github.com/ai-job-search-dev/ai-job-search/issues/37 +[#39]: https://github.com/ai-job-search-dev/ai-job-search/issues/39 +[#41]: https://github.com/ai-job-search-dev/ai-job-search/issues/41 +[#43]: https://github.com/ai-job-search-dev/ai-job-search/issues/43 +[#44]: https://github.com/ai-job-search-dev/ai-job-search/issues/44 +[#49]: https://github.com/ai-job-search-dev/ai-job-search/issues/49 +[#52]: https://github.com/ai-job-search-dev/ai-job-search/issues/52 +[#54]: https://github.com/ai-job-search-dev/ai-job-search/issues/54 +[#55]: https://github.com/ai-job-search-dev/ai-job-search/issues/55 +[#56]: https://github.com/ai-job-search-dev/ai-job-search/issues/56 +[#59]: https://github.com/ai-job-search-dev/ai-job-search/issues/59 +[#60]: https://github.com/ai-job-search-dev/ai-job-search/issues/60 +[#63]: https://github.com/ai-job-search-dev/ai-job-search/issues/63 +[#64]: https://github.com/ai-job-search-dev/ai-job-search/issues/64 +[#66]: https://github.com/ai-job-search-dev/ai-job-search/issues/66 +[#67]: https://github.com/ai-job-search-dev/ai-job-search/issues/67 +[#68]: https://github.com/ai-job-search-dev/ai-job-search/issues/68 +[#72]: https://github.com/ai-job-search-dev/ai-job-search/issues/72 +[#73]: https://github.com/ai-job-search-dev/ai-job-search/issues/73 +[#75]: https://github.com/ai-job-search-dev/ai-job-search/issues/75 +[#76]: https://github.com/ai-job-search-dev/ai-job-search/issues/76 +[#155]: https://github.com/ai-job-search-dev/ai-job-search/pull/155 +[#162]: https://github.com/ai-job-search-dev/ai-job-search/pull/162 +[#165]: https://github.com/ai-job-search-dev/ai-job-search/pull/165 diff --git a/README.md b/README.md index c46aae58..db875a5b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ MadsLorentzen%2Fai-job-search | Trendshift

    -[![CI](https://github.com/MadsLorentzen/ai-job-search/actions/workflows/ci.yml/badge.svg)](https://github.com/MadsLorentzen/ai-job-search/actions/workflows/ci.yml) +[![CI](https://github.com/ai-job-search-dev/ai-job-search/actions/workflows/ci.yml/badge.svg)](https://github.com/ai-job-search-dev/ai-job-search/actions/workflows/ci.yml) An AI-powered job application framework built on [Claude Code](https://claude.com/claude-code). Fork it, fill in your profile, and let Claude evaluate job postings, tailor your CV, write cover letters, and prepare you for interviews. @@ -61,7 +61,7 @@ The framework encodes career guidance best practices, including structured evalu ## Prerequisites -- [Claude Code](https://claude.com/claude-code) (CLI). Claude Code has no free tier: you need a Claude Pro/Max/Team subscription or Anthropic API credits (pay-per-token, usually cheaper for occasional use). Using a different agent tool (Codex, Antigravity, Gemini CLI)? Start at [`AGENTS.md`](AGENTS.md) - the portal search skills work there out of the box, and [community forks](https://github.com/MadsLorentzen/ai-job-search/discussions/78) adapt the full workflow. +- [Claude Code](https://claude.com/claude-code) (CLI). Claude Code has no free tier: you need a Claude Pro/Max/Team subscription or Anthropic API credits (pay-per-token, usually cheaper for occasional use). Using a different agent tool (Codex, Antigravity, Gemini CLI)? Start at [`AGENTS.md`](AGENTS.md) - the portal search skills work there out of the box, and [community forks](https://github.com/ai-job-search-dev/ai-job-search/discussions/78) adapt the full workflow. - Python 3.10+ - [Bun](https://bun.sh) (for job search CLI tools) - LaTeX distribution with `lualatex` and `xelatex`: [TeX Live](https://tug.org/texlive/), [MacTeX](https://tug.org/mactex/), [TinyTeX](https://yihui.org/tinytex/), or [MiKTeX](https://miktex.org/). The CV compiles with `lualatex` (pdflatex often fails on modern MiKTeX installs with `fontawesome5` font-expansion errors); the cover letter compiles with `xelatex` because `cover.cls` requires `fontspec`. If using a minimal TeX install such as TinyTeX or BasicTeX, install the extra packages listed in [SETUP.md](SETUP.md#minimal-tex-install-tinytexbasictex). @@ -74,7 +74,7 @@ The framework encodes career guidance best practices, including structured evalu ### 1. Fork and clone ```bash -gh repo fork MadsLorentzen/ai-job-search --clone +gh repo fork ai-job-search-dev/ai-job-search --clone cd ai-job-search ``` @@ -302,7 +302,7 @@ The four Danish CLI tools in `.agents/skills/` (Jobbank, Jobdanmark, Jobindex, J Give it your local job board's URL. The command investigates the portal (search-URL pattern, result-page structure, robots.txt/access rules), scaffolds a CLI skill with the same structure, commands, and output contract as the shipped ones, and test-runs a live query before registering anything. Auth-walled portals are declined, and portals with restrictive terms get a prominent personal-use-only warning in the generated skill. The generated skill is market-specific and lives in your fork; the generator itself is the universal part. -Maintaining a fork adapted to your market or language? Add it to the [Community forks & adaptations](https://github.com/MadsLorentzen/ai-job-search/discussions/78) thread so others can find it. +Maintaining a fork adapted to your market or language? Add it to the [Community forks & adaptations](https://github.com/ai-job-search-dev/ai-job-search/discussions/78) thread so others can find it. For **country-agnostic** starting points outside Denmark, the repo ships two portal skills alongside the Danish demos: @@ -313,11 +313,11 @@ For **country-agnostic** starting points outside Denmark, the repo ships two por Everything above adds up to an extension model, so here it is stated plainly. The framework has three extension points, and none of them require touching upstream: -1. **Portal skills** - the module system for job boards. Every `*-search` skill is a self-contained folder under `.agents/skills/` with the same contract (a `search`/`detail` CLI, `--format json|table|plain` output, an `enabled:` flag in its `SKILL.md`, its own tests). `/scrape` auto-discovers any installed skill that follows the contract - nothing to register, nothing to wire up. `/add-portal` generates new ones; the [community portal index](https://github.com/MadsLorentzen/ai-job-search/discussions/78) catalogs the ones other forks have built. +1. **Portal skills** - the module system for job boards. Every `*-search` skill is a self-contained folder under `.agents/skills/` with the same contract (a `search`/`detail` CLI, `--format json|table|plain` output, an `enabled:` flag in its `SKILL.md`, its own tests). `/scrape` auto-discovers any installed skill that follows the contract - nothing to register, nothing to wire up. `/add-portal` generates new ones; the [community portal index](https://github.com/ai-job-search-dev/ai-job-search/discussions/78) catalogs the ones other forks have built. 2. **Document templates** - `/add-template` registers any CV or cover-letter toolchain that compiles to PDF from the command line, LaTeX or otherwise. 3. **Evaluation criteria** - deal-breakers and preferences in your profile are free-form, and the evaluation rubric scores against whatever you put there. "Strong parental-leave terms", "minimum salary X per my union's scale", "no on-call" - each is one profile line, no code, and it carries real weight in `/rank` and `/apply` fit evaluations. Language is the one deal-breaker type with dedicated, structured handling: `/setup` captures every language you work in and your level (asked directly, or inferred from your CV/LinkedIn export) into a `Languages` table, and the Language Gate (`04-job-evaluation.md`) hard-rejects a posting that requires a language you haven't declared at all, while flagging - not auto-rejecting - one that asks for a higher level than you declared in a language you do work in, so a borderline case (a strict "fluent" bar against your own B1/B2, say) gets your judgment instead of a silent drop. -**Borrowing a portal skill from another fork** is the intended way to get a board that upstream doesn't ship: find it in the [portal index](https://github.com/MadsLorentzen/ai-job-search/discussions/78), open that fork, and copy the one folder into your own `.agents/skills/`. Before you run it: +**Borrowing a portal skill from another fork** is the intended way to get a board that upstream doesn't ship: find it in the [portal index](https://github.com/ai-job-search-dev/ai-job-search/discussions/78), open that fork, and copy the one folder into your own `.agents/skills/`. Before you run it: - **Read the code.** All of it - these CLIs run pre-approved on your machine (`.claude/settings.json` allowlists them) against your career data. Check that the only network calls go to the job board it claims to search, that `package.json` has no `dependencies` and no lifecycle scripts (`postinstall` etc.), and that nothing reads or writes outside its own folder. - **Run its tests offline** (`bun test` in the skill's `cli/` directory) - a well-built skill's tests pass with no network access. @@ -325,7 +325,7 @@ Everything above adds up to an extension model, so here it is stated plainly. Th The copy step is manual on purpose. Your settings already allow installed portal skills to run without asking each time - so an installer that fetched them from third-party repos for you would skip the one check that matters: you, reading the code first. There isn't one, and that's a security decision rather than a missing feature. -Market-specific *data sources* (a national salary database, local award-rate tables) follow the same pattern as portals: they belong in a market fork, shared via [#78](https://github.com/MadsLorentzen/ai-job-search/discussions/78), not upstream. +Market-specific *data sources* (a national salary database, local award-rate tables) follow the same pattern as portals: they belong in a market fork, shared via [#78](https://github.com/ai-job-search-dev/ai-job-search/discussions/78), not upstream. ### Salary benchmarking diff --git a/SECURITY.md b/SECURITY.md index 23e015e8..af7d4277 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,7 +2,7 @@ ## Reporting a vulnerability -Please report security findings privately via **[GitHub private vulnerability reporting](https://github.com/MadsLorentzen/ai-job-search/security/advisories/new)** rather than a public issue. You will get a response within a few days, credit in the fix unless you prefer otherwise, and public disclosure coordinated with the patch. +Please report security findings privately via **[GitHub private vulnerability reporting](https://github.com/ai-job-search-dev/ai-job-search/security/advisories/new)** rather than a public issue. You will get a response within a few days, credit in the fix unless you prefer otherwise, and public disclosure coordinated with the patch. If the private form is unavailable, open a public issue that describes the *class* of problem without a working recipe, and note that you have details to share privately. @@ -47,4 +47,4 @@ Instruction-level defenses raise the bar; permission-level defenses bound the da ## Scope notes - Portal CLI skills make live requests only when you run them; CI never does. -- Community fork skills listed in the [forks index](https://github.com/MadsLorentzen/ai-job-search/discussions/78) are **not** covered by this policy - review the code you copy, as the index itself says. +- Community fork skills listed in the [forks index](https://github.com/ai-job-search-dev/ai-job-search/discussions/78) are **not** covered by this policy - review the code you copy, as the index itself says. diff --git a/SETUP.md b/SETUP.md index e504360f..67592068 100644 --- a/SETUP.md +++ b/SETUP.md @@ -166,7 +166,7 @@ If a command still uses `pdftotext -layout`, it must pass `-enc UTF-8` as well. ## 2. Fork and clone ```bash -gh repo fork MadsLorentzen/ai-job-search --clone +gh repo fork ai-job-search-dev/ai-job-search --clone cd ai-job-search gh repo set-default /ai-job-search ``` @@ -307,10 +307,10 @@ Upstream keeps improving the methodology files your fork has personalized, so pl **Prefer releases over raw `master`.** Tagged [releases](../../releases) are vetted checkpoints, each described in [CHANGELOG.md](CHANGELOG.md). Updating to a tag pulls a stable, documented state instead of whatever `master` happens to be mid-review. Fetch tags with `git fetch upstream --tags` and merge a release (for example `git merge v1.0.0`) when you want stability; pull `master` directly only when you specifically want the latest unreleased changes. The steps below apply either way - substitute the release tag for `upstream/master` where you see it. -1. **Commit your personalization - but know where those commits land.** `/setup` edits CLAUDE.md and the profile skill files in place; those edits are *yours*, and committing them is what lets updates merge cleanly. But a GitHub **fork of this repo is public** - forks of public repositories cannot be made private - so anything you commit *and push to a fork* is visible to anyone. If you want your profile in a remote at all, don't push it to a fork: create a **private** repository, push there, and add this repo as the `upstream` remote (`git remote add upstream https://github.com/MadsLorentzen/ai-job-search.git`) to keep receiving updates. Committing locally without pushing is also fine. The genuinely sensitive files (tracker, salary data, `documents/`, application archives) are gitignored and never enter git either way. An uncommitted working tree is the most common reason `git pull` refuses to merge at all (`Your local changes ... would be overwritten`). +1. **Commit your personalization - but know where those commits land.** `/setup` edits CLAUDE.md and the profile skill files in place; those edits are *yours*, and committing them is what lets updates merge cleanly. But a GitHub **fork of this repo is public** - forks of public repositories cannot be made private - so anything you commit *and push to a fork* is visible to anyone. If you want your profile in a remote at all, don't push it to a fork: create a **private** repository, push there, and add this repo as the `upstream` remote (`git remote add upstream https://github.com/ai-job-search-dev/ai-job-search.git`) to keep receiving updates. Committing locally without pushing is also fine. The genuinely sensitive files (tracker, salary data, `documents/`, application archives) are gitignored and never enter git either way. An uncommitted working tree is the most common reason `git pull` refuses to merge at all (`Your local changes ... would be overwritten`). 2. **Preview what changed before pulling:** ```bash - git remote add upstream https://github.com/MadsLorentzen/ai-job-search.git # first time only, if you cloned your own fork + git remote add upstream https://github.com/ai-job-search-dev/ai-job-search.git # first time only, if you cloned your own fork git fetch upstream # or origin, if you cloned the template directly python3 tools/check_upstream_updates.py ``` diff --git a/tests/_upstream.py b/tests/_upstream.py index 7fc2351e..bd40289d 100644 --- a/tests/_upstream.py +++ b/tests/_upstream.py @@ -10,7 +10,7 @@ import os import subprocess from pathlib import Path -UPSTREAM = "MadsLorentzen/ai-job-search" +UPSTREAM = "ai-job-search-dev/ai-job-search" REPO = Path(__file__).resolve().parent.parent diff --git a/tests/test_check_upstream_updates.py b/tests/test_check_upstream_updates.py index 640e4473..a31bbdf8 100644 --- a/tests/test_check_upstream_updates.py +++ b/tests/test_check_upstream_updates.py @@ -10,7 +10,7 @@ from pathlib import Path REPO_ROOT = Path(__file__).resolve().parent.parent SCRIPT = REPO_ROOT / "tools" / "check_upstream_updates.py" -TEMPLATE_URL = "https://github.com/MadsLorentzen/ai-job-search.git" +TEMPLATE_URL = "https://github.com/ai-job-search-dev/ai-job-search.git" FORK_URL = "https://github.com/octocat/ai-job-search.git" FRAMEWORK_FILES = [ diff --git a/tests/test_tools_utf8_output.py b/tests/test_tools_utf8_output.py index d0689eac..32ca3029 100644 --- a/tests/test_tools_utf8_output.py +++ b/tests/test_tools_utf8_output.py @@ -158,7 +158,7 @@ class ToolsWriteUtf8(unittest.TestCase): subprocess.run(["git", "init", "-b", "master"], cwd=repo, check=True, capture_output=True) subprocess.run(["git", "config", "user.name", "Test"], cwd=repo, check=True, capture_output=True) subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=repo, check=True, capture_output=True) - subprocess.run(["git", "remote", "add", "upstream", "https://github.com/MadsLorentzen/ai-job-search.git"], cwd=repo, check=True, capture_output=True) + subprocess.run(["git", "remote", "add", "upstream", "https://github.com/ai-job-search-dev/ai-job-search.git"], cwd=repo, check=True, capture_output=True) (repo / "file.txt").write_text("hello\n", encoding="utf-8") subprocess.run(["git", "add", "file.txt"], cwd=repo, check=True, capture_output=True) subprocess.run(["git", "commit", "-m", "init"], cwd=repo, check=True, capture_output=True) diff --git a/tests/test_upstream_checkout.py b/tests/test_upstream_checkout.py index 58a777ee..7894d4ae 100644 --- a/tests/test_upstream_checkout.py +++ b/tests/test_upstream_checkout.py @@ -16,7 +16,7 @@ except ImportError: # `python -m unittest discover -s tests` imports test modul import _upstream from _upstream import is_upstream_checkout -UPSTREAM = "MadsLorentzen/ai-job-search" +UPSTREAM = "ai-job-search-dev/ai-job-search" def _no_github_env(testcase): @@ -81,16 +81,16 @@ class UpstreamCheckoutTests(unittest.TestCase): def test_origin_url_forms(self): cases = [ - ("https://github.com/MadsLorentzen/ai-job-search", True), - ("https://github.com/MadsLorentzen/ai-job-search.git", True), - ("git@github.com:MadsLorentzen/ai-job-search", True), - ("git@github.com:MadsLorentzen/ai-job-search.git", True), - ("ssh://git@github.com/MadsLorentzen/ai-job-search.git", True), - ("ssh://git@github.com/MadsLorentzen/ai-job-search", True), + ("https://github.com/ai-job-search-dev/ai-job-search", True), + ("https://github.com/ai-job-search-dev/ai-job-search.git", True), + ("git@github.com:ai-job-search-dev/ai-job-search", True), + ("git@github.com:ai-job-search-dev/ai-job-search.git", True), + ("ssh://git@github.com/ai-job-search-dev/ai-job-search.git", True), + ("ssh://git@github.com/ai-job-search-dev/ai-job-search", True), ("ssh://git@github.com/someone-else/ai-job-search.git", False), ("https://github.com/someone-else/ai-job-search.git", False), ("git@github.com:someone-else/ai-job-search", False), - ("https://gitlab.com/MadsLorentzen/ai-job-search.git", False), + ("https://gitlab.com/ai-job-search-dev/ai-job-search.git", False), ] for url, expected in cases: with self.subTest(url=url): diff --git a/tests/test_upstream_triage.py b/tests/test_upstream_triage.py index e3ff42c1..8c00f20c 100644 --- a/tests/test_upstream_triage.py +++ b/tests/test_upstream_triage.py @@ -9,7 +9,7 @@ from pathlib import Path REPO_ROOT = Path(__file__).resolve().parent.parent SCRIPT = REPO_ROOT / "tools" / "upstream_triage.py" WORKFLOW = REPO_ROOT / ".github" / "workflows" / "upstream-watch.yml" -UPSTREAM_SLUG = "MadsLorentzen/ai-job-search" +UPSTREAM_SLUG = "ai-job-search-dev/ai-job-search" def git(root: Path, *args: str) -> str: diff --git a/tools/check_upstream_updates.py b/tools/check_upstream_updates.py index fa315eba..254b0d0e 100755 --- a/tools/check_upstream_updates.py +++ b/tools/check_upstream_updates.py @@ -35,7 +35,7 @@ FRAMEWORK_FILES = [ "AGENTS.md", ] -UPSTREAM_REPO_SLUG = "MadsLorentzen/ai-job-search" +UPSTREAM_REPO_SLUG = "ai-job-search-dev/ai-job-search" def run_git(args: list[str]) -> tuple[int, str, str]: res = subprocess.run(