Git hooks and CI
The same rule reports the same line the same way in an agent hook, a git hook and CI. What counts as new is a match on a line you changed; a violation that was already there is backlog, shown and never failing.
- Pre-commit:
rulecast runwith no file flags checks the staged files, reads their content from the index (what is being committed, not the working tree), and judges it againstHEAD.llmrules are skipped unless you pass--llm, and one line says so: a commit should not cost money. - Pre-push and CI:
rulecast run --from-ref A --to-ref Bchecks the files changed between the merge base withAandB, reading them atB. Without--to-refit reads the working tree. rulecast run --files F...and--all-filesjudge files whole: against a committed.rulecast/baseline.jsonwhen there is one, else with no baseline. Use the hook below rather than--filesfrom a hook manager.- A nightly sweep or a scheduled agent loop over the whole repository has no ref to diff against: commit a baseline once, and
rulecast run --all-filesexits 0 unless something got worse. Recipes for/goal,/loopand scheduled sweeps:agents/reference/loops.md.
rulecast installs no git hooks itself: lefthook, husky and pre-commit already own .git/hooks. Add @syv-ai/rulecast to the project’s devDependencies (the agent hooks need it there anyway), and wire it into the one you use.
pre-commit (.pre-commit-config.yaml):
minimum_pre_commit_version: "3.2.0"repos: - repo: https://github.com/syv-ai/rulecast rev: v0.4.0 hooks: - id: rulecast - id: rulecast-pushlefthook (lefthook.yml):
pre-commit: jobs: - name: rulecast run: npx --no-install rulecast runpre-push: jobs: - name: rulecast run: npx --no-install rulecast run --from-ref "$(git merge-base origin/HEAD HEAD)" --to-ref HEADGitHub Actions:
on: pull_requestjobs: rulecast: runs-on: ubuntu-latest permissions: contents: read security-events: write steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # rulecast needs the merge base with the target branch - uses: actions/setup-node@v4 with: node-version: 22 - run: npm ci - run: npx rulecast run --from-ref origin/${{ github.base_ref }} --format sarif > rulecast.sarif - uses: github/codeql-action/upload-sarif@v3 if: always() # rulecast exits 1 on findings and 3 when a rule could not run, which is when the upload matters with: sarif_file: rulecast.sarifA shallow clone has no merge base; rulecast says so and names fetch-depth: 0. --from-ref runs llm rules, which judge changed files only.
command scripts, and oxlint, are handed a scratch copy of each file in staged and --to-ref runs, because the content to judge is not the file on disk. ruff and eslint get it on stdin under the real path, so path-keyed configuration still applies. Whole-project tools (scope: project, hook rules) need the real project: they run on the working tree when the rule’s files there match what is being committed, and are skipped with a warning naming the files when they don’t. rulecast never stashes.
Fresh environments
Section titled “Fresh environments”A CI job, a cloud agent session or a new container starts with nothing installed. rulecast does not fail quietly there: in unattended mode every stop says which rules did not run and why (“2 rules disabled: …: not in the cache”), and the agent is held once per prompt to put that in its final report. rulecast run says incomplete and lists them under disabled in JSON. To have nothing to report:
- Install dependencies (
npm ciorpnpm install) before the agent starts. Without them the agent hooks do nothing, and in unattended mode every stop says so. - Run
npx rulecast install. It fetches the pinned rule repos; hooks never fetch. - Run
npx rulecast doctor --cibefore the agent step. It reads the cache as the hooks do and fetches nothing. It exits 2 if a rule repo is missing from the cache, a rule’s tool is not installed, or the hooks cannot run (a committed hook command runs onlynode_modules/.bin/rulecast, so a global ornpxrulecast does not count). Add--from-ref origin/mainwhen the job also runsrulecast run --from-ref. - Check out with
fetch-depth: 0if you use--from-ref. - Under
CIorGITHUB_ACTIONSunattended mode is on by itself. Elsewhere setRULECAST_MODE=unattended. The hook command’s not-installed guard reads only the environment, never the config’smode:. - End the loop with
rulecast run --from-ref …. It is the gate that counts, and a non-zero exit withincompletemeans some rules did not run. - Optionally cache
~/.cache/rulecast/reposbetween jobs (for exampleactions/cachekeyed on.rulecast-config.yaml). It saves a clone per job.
A new git worktree on the same machine needs none of this beyond step 1. Rule repos are cached per user, not per checkout, and nothing on the edit path reads a per-project cache (Performance). rulecast warm only matters for plugin detectors that warm, and SessionStart starts it for you.