CLAUDE.md@cli · git:20260924.fa28c43 · 2026-09-24 · sha256 f3ec3de776b74838

CLAUDE.md@cli git:20260924.fa28c43A

Immutable. This exact content never changes and is served at /api/v1/blob/f3ec3de776b74838.

# Working in `cli/`

This directory contains the POSIX `eklavya-gate` script. The `eklavya` binary
(`config`, `doctor`, `install`, `dashboard`, etc.) lives in `mcp/src/cli.ts`.
Read the root `CLAUDE.md` for the repository contract.

## Installation and scope

The terminal gate requires both a per-repository installation and project
`quiz.enforced: true`. `eklavya install` does not install a git hook.

`scripts/install-git-hook.sh` writes into the common Git directory so linked
worktrees use the same hook. It preserves an existing hook as `pre-commit.local`
and runs that first; `--uninstall` restores it. With `core.hooksPath` configured,
it prints the integration line and exits 2 without installing anything.

The installed wrapper tries the managed runtime, then the original installation
path, and invokes the script with `sh`. Missing both paths warns and allows the
commit. Preserve this fallback: updates can move plugin directories.
`eklavya uninstall` warns about the hook and prints removal instructions; it
does not remove the hook itself.

Project config lives in `~/.eklavya/projects/<slug>/config.json`; worktrees fold
to their main checkout. The script reads the old `<repo>/.eklavya.json` only as
a fallback and never migrates it. Global enforcement alone does not activate
this gate, but a global `quiz.enabled: false` releases it unless overridden by
the project. Keep legacy `mode` compatibility and explicit `false` values.

## Fail-open contract

Use POSIX `sh`; the installed hook runs through `/bin/sh`. Missing repository,
config, `jq`, `sqlite3`, database or gate row allows the commit. Database query
errors also allow it. Missing dependencies warn on stderr.

Exit 1 only when a gate row exists for this repository and `passed` is not `1`.
Do not add an operational error that prevents a commit.

## Keep duplicated logic aligned

| Script responsibility | Source and verification |
|---|---|
| Config merge and legacy `mode` alias | `mcp/src/config.ts`; shared fixtures in `mcp/test/gate.test.ts` |
| Project slug and worktree folding | `mcp/src/paths.ts`; physical paths must also handle symlinks |
| Latest repository gate lookup | `gates` schema and `syncGate` in `mcp/src/store.ts` |
| In-session commit detection | `mcp/src/hooks/pre-tool-gate.ts` and `commit-lib.ts` |

The script trusts persisted `passed`; it must not duplicate passing-grade or
threshold arithmetic. It looks up the latest gate by repository, whereas the
in-session hook uses a session ID. In jq, use explicit `if`/`elif` for booleans:
`//` treats `false` as absent and can re-enable a disabled legacy gate.

## Documentation and verification

In the same PR, update the manual `commit-gate.mdx` for installer, prerequisite,
scope, bypass or failure behavior changes. Check `configuration.mdx`,
`skills/setup/SKILL.md` and the commit-gate acceptance check in `CONTRIBUTING.md`
when their claims change. Every claim about terminal enforcement must include
both installation and project enforcement.

From `mcp/`, run `npm test -- gate`. It builds the runtime and exercises the real
script and installer in temporary repositories, including an actual commit.
Build `web/` after documentation changes; report any live acceptance test not run.