Skip to content
Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

Adjacent

Release CI License: MIT Used By

Adjacent is a GitHub Action that adds related repositories to your README. It compares topics and README text across public repositories owned by the same user or organization. Use it to help readers discover related projects in your portfolio or ecosystem.

Adjacent edits only the section you mark. It preserves the rest of the file, leaves the README untouched if discovery fails, and reports the scores behind each recommendation in the workflow summary. It runs without a paid API or language model.

Setup

Version 2 requires explicit README markers. When upgrading from version 1, follow the migration instructions below before enabling writes.

Mark the section

Add one start comment and one end comment to an existing README, each on its own line:

<!-- adjacent:start -->

<!-- adjacent:end -->

Adjacent replaces everything between these comments. Put any heading you want it to manage inside the markers. Missing, repeated, or reversed markers stop the action before discovery. The README must already exist inside the checkout.

Add a workflow

Save this as .github/workflows/adjacent.yml:

name: Related repositories
on:
  schedule:
    - cron: '0 5 * * 0'
  workflow_dispatch:
permissions:
  contents: write
concurrency:
  group: adjacent-${{ github.ref }}
  cancel-in-progress: false
jobs:
  recommend:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7.0.1
      - uses: gojiplus/adjacent@v2.0
        id: adjacent
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      - name: Commit changes
        if: steps.adjacent.outputs.changed == 'true'
        run: |
          git config user.name 'github-actions[bot]'
          git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
          git add README.md
          if ! git diff --cached --quiet; then
            git commit -m 'Update adjacent repositories'
            git push
          fi

The action fetches data and edits the local file. The caller owns committing and pushing. Use contents: read and omit the commit step for a preview or artifact-only workflow; branch protection may require your own pull-request workflow instead of a direct push. If you change readme_path, change the path in git add too.

Set dry_run: 'true' and omit the commit step to preview the proposed README in the job log without changing it. Discovery still uses the GitHub API.

An existing user of Adjacent is fox_news_transcripts.

Inputs and outputs

Input Default Meaning
token Required Token for GitHub API access; usually secrets.GITHUB_TOKEN.
repo Current repository Target in owner/name form. Candidate repositories belong to this owner.
similarity_method combined topics, readme, or combined.
topic_weight 0.6 Topic share in combined mode, from 0 to 1.
exclude_repos Empty Comma-separated short names or owner/name; case-insensitive.
max_repos 5 Positive integer limiting displayed recommendations.
readme_path README.md Existing marked file within the checkout.
min_score 0.1 Only scores strictly above this threshold qualify; from 0 to 1.
include_forks false Allow public forks.
include_archived false Allow public archived repositories.
include_templates false Allow public template repositories.
dry_run false Compute and display proposed content without writing.

Boolean inputs accept true and false. Private and disabled repositories, the target itself, and explicit exclusions are always omitted. An opt-in changes only its corresponding filter: an archived fork needs both opt-ins.

Output Meaning
changed true when proposed content differs from the local README, including during dry runs.
recommendation_count Number of selected recommendations after thresholding and truncation.

The job summary reports total scores, topic scores, README scores, and shared topics. A successful search with no qualifying results replaces stale recommendations with “No related repositories found.”

How ranking works

Topic similarity is the fraction of distinct topics shared by the two repositories: the intersection divided by the union. README similarity uses cosine similarity from one TF-IDF model fitted across the target and eligible candidates. TF-IDF gives less weight to terms common across that collection. Generated Adjacent sections, images, badges, and code are removed before scoring; useful link text stays.

Combined mode uses topic_weight × topic_score + (1 − topic_weight) × readme_score. If the target has no topics or no usable README text, the available signal receives all the weight. A candidate missing a signal gets zero for that component. Explicit topics and readme modes use only the requested signal. If neither signal is available, no repositories qualify. English stop words are removed; text similarity is not a semantic or multilingual model.

Scores are not probabilities and are not rescaled to make the best candidate score 1. Changing the candidate collection can change TF-IDF scores. Equal scores are ordered by repository name, so identical API data produces identical output. README text comes from GitHub's default-branch README, even if readme_path points to another local file.

API calls have timeouts and up to three retries for transient failures. Rate-limit waits honor GitHub headers; waits over five minutes fail with a retry-later message. Missing READMEs contribute no text. Authentication errors, malformed responses, and exhausted retries fail the run without modifying the README.

Migrating from earlier releases

Wrap the existing generated section, including its heading and attribution, in the marker pair shown above. Keep manually written content outside it. Heading-based replacement and automatic method fallback have been removed.

Review recommendations with dry_run before enabling writes. Default filtering now excludes archives, forks, and templates, and raw scoring can produce fewer recommendations than earlier versions. Use the opt-ins or adjust min_score if needed. The action runs Python 3.13; development checks also cover Python 3.14.

Development

python3.13 -m venv .venv
.venv/bin/python -m pip install -r requirements-dev.txt
make check PYTHON=.venv/bin/python

Install actionlint for workflow checks. make check runs Black, isort, flake8, pytest, and actionlint; make format applies Python formatting. Tests mock GitHub and do not need a token.

With Docker running:

make ci-docker
make ci-docker PYTHON_VERSION=3.14

These commands use standard Python images and the upstream actionlint image, with the checkout mounted read-only. CI runs the same Python checks for both supported versions.

Dependency inputs live in requirements.in and requirements-dev.in; compiled files pin the complete environment. Refresh them with uv:

uv pip compile --upgrade --python-version 3.13 requirements.in -o requirements.txt
uv pip compile --upgrade --python-version 3.13 requirements-dev.in -o requirements-dev.txt

For a read-only live smoke test, export GITHUB_TOKEN through your normal credential setup, then run from this checkout:

GITHUB_REPOSITORY=gojiplus/adjacent SIMILARITY_METHOD=topics DRY_RUN=true .venv/bin/python -m adjacent

The entrypoint reads the uppercase equivalents of action inputs, except repo and token, which use GITHUB_REPOSITORY and GITHUB_TOKEN. GITHUB_WORKSPACE sets the checkout root; it defaults to the current directory. This internal module is run directly from the action checkout and is not published as a Python package.

🔗 Adjacent Repositories

Powered by Adjacent

About

Add related repositories to your readme

Topics

Resources

Stars

7 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages