context-forgev1.0.0
User guide

Installation & setup

Get the context-forge binary onto a Linux machine, make shell completion actually work, register the background service and (optionally) plug in a local Ollama model.

Requirements

Packages ship the binary and the shell completions only. The rule plugins (trigger_default.so, pre-rule_ansi.so, rule_*.so) are not packaged in v1.0.0: without them the server loads no rules and only the LLM pass can run. To use rules, build the plugins from source once and point setup --plugins at the resulting plugins/ folder (or write your own).

Install from packages

Signed .rpm and .deb packages are published on the project's GitHub Pages. Two channels exist: context-forge (stable, built from vX.Y.Z tags) and context-forge-pre (pre-release, built from vX.Y.Z-something tags). They conflict with each other, so install one or the other.

The repository is laid out as fedora/$releasever/$basearch; Fedora 44 / x86_64 is what CI currently publishes.

sudo curl -fsSL -o /etc/yum.repos.d/context-forge.repo \
     https://tsukini22.github.io/context-forge/context-forge.repo
sudo rpm --import https://tsukini22.github.io/context-forge/RPM-GPG-KEY-tsukini
sudo dnf install context-forge        # or: context-forge-pre
sudo dnf install libconfig            # runtime dependency, if not already present

The .repo file it downloads is simply:

[context-forge]
name=context-forge repository
baseurl=https://tsukini22.github.io/context-forge/fedora/$releasever/$basearch
enabled=1
gpgcheck=1
gpgkey=https://tsukini22.github.io/context-forge/RPM-GPG-KEY-tsukini

If dnf complains about an invalid hash after a new release, clear its cache: sudo dnf clean all && sudo dnf makecache.

The APT repository serves stable main for amd64 and is signed with the same key.

sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://tsukini22.github.io/context-forge/RPM-GPG-KEY-tsukini \
  | gpg --dearmor | sudo tee /etc/apt/keyrings/context-forge.gpg > /dev/null
sudo chmod a+r /etc/apt/keyrings/context-forge.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/context-forge.gpg] \
https://tsukini22.github.io/context-forge/debian stable main" \
  | sudo tee /etc/apt/sources.list.d/context-forge.list > /dev/null

sudo apt-get update
sudo apt-get install context-forge   # or: context-forge-pre

The .deb is produced by the same Fedora-based CI container as the .rpm and declares no dependencies. It expects libconfig++.so.15 and a recent glibc/libstdc++; if context-forge -h fails with a missing shared library on your Debian/Ubuntu release, build from source instead.

You can also fetch a package file directly and install it with your package manager. The naming pattern is context-forge[-pre]-<version>-Linux.<rpm|deb>.

# RPM
curl -fLO https://tsukini22.github.io/context-forge/fedora/44/x86_64/context-forge-1.0.0-Linux.rpm
sudo dnf install ./context-forge-1.0.0-Linux.rpm

# DEB
curl -fLO https://tsukini22.github.io/context-forge/debian/pool/main/context-forge-1.0.0-Linux.deb
sudo apt-get install ./context-forge-1.0.0-Linux.deb

Browse the index at tsukini22.github.io/context-forge (the gh-pages branch of the repository).

Build from source

Building from source is required to get the plugins, and is the only way to install on distributions without a package. The build is CMake ≥ 3.20 and forces clang++ (set in CMakeLists.txt), C++20.

Build dependencies

DependencyPurposeFedoraDebian / Ubuntu
clang, cmake, make, pkg-configtoolchainclang cmake make pkgconf-pkg-configclang cmake make pkg-config
libutils ≥ 2.13.22author's utility library (argument parser, process/pipe/shared-memory wrappers, exceptions, observer)see below — installed from the author's own package repository
cpp-httplibHTTP client to Ollamacpp-httplib-devellibcpp-httplib-dev
libconfig++parses .cfg rule fileslibconfig-devellibconfig++-dev
nlohmann/jsonOllama request/response bodiesjson-develnlohmann-json3-dev
Python 3generates include/exception/generated_external_exception_header.hpp at configure timepython3python3
GoogleTest (tests only)-DBUILD_TESTS=ONgtest-devellibgtest-dev
ccache (optional)picked up automatically if presentccacheccache

libutils is published the same way as context-forge. Its setup.sh registers the repository for your distribution; then install the headers/CMake config (libutils-dev) and the optimized library (libutils-op) — or just the meta-package:

curl -fsSL https://raw.githubusercontent.com/TsukiNi22/libutils/main/setup.sh | bash -s
sudo dnf install libutils            # Fedora  (pre-release channel: libutils-pre)
sudo apt-get install libutils        # Debian  (pre-release channel: libutils-pre)

CMake asks for utils 2.13.22 or newer. At the time of writing that version only exists on the -pre channel, so use libutils-pre if the stable package is older. You can also build libutils from its repository (cmake --build build --target install) into /usr/local.

Build

git clone https://github.com/TsukiNi22/context-forge.git
cd context-forge
make                     # = cmake -S . -B build && cmake --build build --parallel

Outputs land in the source tree, not in build/:

context-forge/
├── context-forge                  ← the executable
└── plugins/
    ├── triggers/trigger_default.so
    ├── pre-rules/pre-rule_ansi.so
    └── rules/rule_ln.so  rule_drop.so  rule_dup.so  rule_insert.so  rule_replace.so

Other useful targets:

CommandEffect
cmake --build build --target releaseReconfigure with -DCMAKE_BUILD_TYPE=Optimized (-O3 -march=native …) and rebuild.
cmake -S . -B build -DCMAKE_BUILD_TYPE=AsanAddressSanitizer build.
cmake --build build --target pluginsBuild only the seven shared objects.
cmake -S . -B build -DBUILD_TESTS=ON && cmake --build build && (cd build && ctest --output-on-failure)Build and run the GoogleTest suite (./unit_tests).
cmake --build build --target package_releaseProduce .rpm/.deb in build/ with CPack.
make re / make fcleanFull rebuild / remove build/.

Install the binary and completions

sudo cmake --install build          # prefix /usr/local

This installs /usr/local/bin/context-forge, /usr/local/share/zsh/site-functions/_context-forge and /usr/local/share/bash-completion/completions/context-forge. Plugins are not installed by this step — keep the plugins/ folder around or let setup --copy clone it (next sections).

context-forge setup writes the absolute path of the binary into the systemd unit and only looks in /usr/local/bin and /usr/bin. Running setup straight from the build directory without installing fails with Missing binary. Either install, or symlink: sudo ln -s "$PWD/context-forge" /usr/local/bin/context-forge.

What gets installed where

FilePackage (/usr)cmake --install (/usr/local)
binary/usr/bin/context-forge/usr/local/bin/context-forge
zsh completion/usr/share/zsh/site-functions/_context-forge/usr/local/share/zsh/site-functions/_context-forge
bash completion/usr/share/bash-completion/completions/context-forge/usr/local/share/bash-completion/completions/context-forge
systemd unit (created by setup)~/.config/systemd/user/context-forge.service
cloned resources (created by setup --copy)~/.config/context-forge/plugins/, ~/.config/context-forge/rules/, ~/.config/context-forge/system-prompt

Shell completion

Both completion scripts are installed for you, but neither shell picks them up automatically in every configuration. Here is what to check for each shell.

zsh only loads a completion file if (1) its directory is in $fpath before compinit runs and (2) compinit is actually called. Frameworks such as oh-my-zsh call compinit for you; a bare ~/.zshrc usually does not.

  1. Check whether the directory is already in $fpath
    print -l $fpath | grep -n 'site-functions'
    which _context-forge        # after compinit: prints the file path if it is picked up

    Distribution zsh builds normally include /usr/share/zsh/site-functions (the package location) and often /usr/local/share/zsh/site-functions (the cmake --install location) — but if you set fpath=(…) yourself in .zshrc without $fpath at the end, those defaults are gone.

  2. Add the directory and initialise completionPut this in ~/.zshrc, before any existing compinit line:
    # pick the one matching how you installed:
    fpath=(/usr/share/zsh/site-functions $fpath)         # package
    fpath=(/usr/local/share/zsh/site-functions $fpath)   # cmake --install
    
    autoload -Uz compinit && compinit
  3. Reload without opening a new shell
    unfunction _context-forge 2>/dev/null
    rm -f ~/.zcompdump*          # zsh caches the list of completion files
    exec zsh

    Then type context-forge and press Tab: you should see the sub-commands (exec, setup, server, …). context-forge exec -Tab lists the flags, and -v Tab offers none basic advanced debug.

Per-user alternative (no root, survives package removal), as suggested in the script header:

mkdir -p ~/.zsh/completions
cp /usr/share/zsh/site-functions/_context-forge ~/.zsh/completions/   # or from the repo: cmake/completions/zsh/_context-forge
# in ~/.zshrc, before compinit:
fpath=(~/.zsh/completions $fpath)
autoload -Uz compinit && compinit

"zsh compinit: insecure directories" warning? zsh refuses group/world-writable directories in $fpath. Fix the permissions (compaudit lists the culprits; chmod go-w them) rather than using compinit -u.

The script is written for the bash-completion framework (≥ 2.x), which lazy-loads completions/context-forge the first time you press Tab after the command name. It searches $XDG_DATA_DIRS (default /usr/local/share:/usr/share), so both install locations are covered — provided the framework is installed and sourced.

  1. Make sure bash-completion is installed and loaded
    sudo dnf install bash-completion        # Fedora
    sudo apt-get install bash-completion    # Debian / Ubuntu
    type _init_completion                   # "is a function" → framework is loaded in this shell

    Interactive login shells source it through /etc/profile.d/bash_completion.sh. If _init_completion is missing, add to ~/.bashrc:

    [ -r /usr/share/bash-completion/bash_completion ] && . /usr/share/bash-completion/bash_completion
  2. Or source the script directly (works without the framework, thanks to its built-in fallback):
    . /usr/share/bash-completion/completions/context-forge          # package
    # . /usr/local/share/bash-completion/completions/context-forge  # cmake --install
  3. Reload and check
    complete -r context-forge 2>/dev/null      # drop a stale registration
    source ~/.bashrc
    complete -p context-forge                  # → complete -F _context_forge_completions context-forge

Per-user copy: mkdir -p ~/.local/share/bash-completion/completions && cp /usr/share/bash-completion/completions/context-forge ~/.local/share/bash-completion/completions/ — new shells pick it up automatically.

The scripts live in the repository under cmake/completions/; you can use them from a clone without installing anything system-wide:

# zsh (current shell only)
fpath+=("$PWD/cmake/completions/zsh"); autoload -Uz compinit && compinit

# bash (current shell only)
source cmake/completions/bash/context-forge

Other shells (fish, nushell…) are not provided in v1.0.0.

Register the daemon

context-forge is split in two: exec (the client you call from your shell) and server (a long-lived process that holds the loaded plugins, rules and Ollama session). setup generates and enables a systemd user unit for the server. Run it as your normal user — not with sudo.

context-forge setup --copy --recursive \
    --plugins ~/src/context-forge/plugins \
    --rules   ~/forge/rules \
    --system-prompt ~/forge/system-prompt   # omit to disable the LLM pass

What setup does, step by step:

  1. Locates the binary (/usr/local/bin then /usr/bin).
  2. With --copy: clones *.so from the plugins directory into ~/.config/context-forge/plugins/, *.cfg into ~/.config/context-forge/rules/ (recursively if --recursive) and the system prompt to ~/.config/context-forge/system-prompt. Without --copy the unit references your directories directly, so edits are picked up on the next restart.
  3. Writes ~/.config/systemd/user/context-forge.service:
    [Unit]
    Description=context-forge daemon
    After=network.target
    
    [Service]
    Type=simple
    Restart=always
    RestartSec=2
    ExecStart=/usr/bin/context-forge server --recursive --plugins /home/you/.config/context-forge/plugins --rules /home/you/.config/context-forge/rules --system-prompt /home/you/.config/context-forge/system-prompt
    
    [Install]
    WantedBy=default.target
  4. Runs systemctl --user daemon-reload, enable and restart on the unit.

Every server option can be passed to setup and is baked into ExecStart: --ip, --port, --model, --verbose LEVEL. Re-run setup whenever you want to change them; it overwrites the unit.

Dry-run before you commit. context-forge check local --plugins … --rules … -v debug loads everything exactly like the server would and prints each plugin and rule file as it is accepted or rejected, then exits. After a setup --copy, context-forge check dist validates the cloned copies.

To start the daemon on boot without an open session (headless machines), enable lingering once: sudo loginctl enable-linger "$USER".

Ollama (optional)

The LLM pass rewrites the (already rule-formatted) output with a model served by Ollama, guided by a system prompt file you provide. It is enabled only when the server was given --system-prompt and the model can be reached at start-up; failures are logged and the pass is skipped until the next restart.

  1. Install Ollama — either from ollama.com or through the wrapper (it runs the official install script and therefore needs root):
    sudo context-forge install-ollama
  2. Pull a model — the default is qwen2.5-coder:1.5b:
    context-forge pull qwen2.5-coder:1.5b
    ollama serve &                          # if Ollama is not already running as a service
  3. Write a system prompt — a plain text file. The server sends your prompt as system and, as the user message, the wrapped command name and its output wrapped in <binary>…</binary> and <output>…</output> tags. Design the prompt around that:
    You receive the output of a shell command inside <output> tags and the program name inside <binary>.
    Return the same information, shortened: keep errors and warnings verbatim, collapse repetitive lines,
    never add commentary. Output plain text only.
  4. Point the server at it (--system-prompt, plus --model/--ip/--port if not default) and restart:
    context-forge setup --copy --recursive --plugins … --rules … --system-prompt ~/forge/system-prompt --model qwen2.5-coder:1.5b
    context-forge status

The server keeps the model loaded by pinging Ollama with keep_alive: 10m on a schedule while it runs, so the first wrapped command after a restart is the only slow one.

Verify the installation

context-forge status
server: running
ollama: installed / running

Then wrap something:

context-forge exec -v advanced -c ls -la

With -v advanced the client prints the raw and formatted blocks around the command output, which is the quickest way to see rules taking effect. If you see [FALLBACK] context-forge: server is unavailable… the command still ran, untouched; check journalctl --user -u context-forge.

Uninstall

context-forge remove                 # stops + disables the unit, deletes it and ~/.config/context-forge
sudo dnf remove context-forge        # or: sudo apt-get remove context-forge
# from-source install: remove the three files listed in "What gets installed where"