Development hygiene guards for OpenCode. It enforces commit discipline, test-before-push, TDD edit order, rare-commit reminders, branch-switch warnings, force-push protection, protected-branch commit warnings, and TODO debt tracking.
The guard core is TypeScript compiled to Node.js ESM. Runtime checks run in-process inside the OpenCode plugin; there is no Bash guard layer.
From this repository:
npm install
npm run install:localThe installer builds the project, copies compiled runtime files into .uplift/dev-discipline, installs a project-local OpenCode loader at .opencode/plugins/uplift-dev-discipline.ts, and appends dev-discipline guidance to AGENTS.md when missing. It does not modify opencode.json.
Installer options are environment variables:
DD_PREFIX=.uplift # install prefix, default: .uplift
DD_DRY_RUN=1 # print actions without changing files
DD_UNINSTALL=1 # remove installed files and the managed OpenCode loader
Validates conventional commit format (type(scope): description), blocks staged secret patterns, and warns when committing code without test files.
Nudge mode (default): Warns before git push / git merge main to run the full test suite. Auto-detects your test runner, suggests the command, and names the branch being pushed/merged.
Gate mode (opt-in): Create .uplift/dev-discipline/test-cmd with your test command. The guard will run it and block push/merge if tests fail.
Warns when uncommitted changes exceed thresholds (default: 5 files or 100 insertions). Severe overflow (3x thresholds) blocks edits/session end to prevent work loss. Set RARE_COMMITS_MODE=ask or RARE_COMMITS_MODE=block to make mild overflow stricter.
Tracks whether tests are written before code in each session. Soft nudge by default; enable TDD mode for hard blocking with TDD_MODE=1 in .uplift/dev-discipline/config or DEV_DISCIPLINE_TDD_MODE=1.
Warns or blocks when switching branches (git checkout, git switch) with uncommitted changes. Blocks on protected branches (main/master), warns on others.
Blocks git push --force to protected branches (main/master). Asks for confirmation on force push to feature branches. Allows --force-with-lease to non-protected branches without interference.
Warns when committing directly to a protected branch (main/master). Encourages feature branch + PR workflow. Allows merge commits and --allow-empty commits.
Counts net new TODO/FIXME/HACK/XXX markers against a session baseline, including untracked files. Warns when new markers appear; asks for confirmation when count exceeds threshold (default: 5), with the top affected files in the message.
OpenCode integration uses a compiled server plugin with tool.execute.before, experimental.chat.system.transform, and session event handling. BLOCK throws an OpenCode tool error. WARN is logged and injected into the next model turn as system context. OpenCode server hooks do not expose a direct interactive ask UI, so ASK blocks by default with an explanatory note.
To make ASK non-blocking in OpenCode, set:
DEV_DISCIPLINE_OPENCODE_ASK_BEHAVIOR=warn
OpenCode apply_patch tracking reads output.args.patchText. OpenCode has no blocking Stop hook; session-end checks run from session.status idle / session.deleted events and are advisory only.
OpenCode adapter knobs:
DEV_DISCIPLINE_OPENCODE_WARN_BEHAVIOR=block # make WARN block
DEV_DISCIPLINE_OPENCODE_FAIL_CLOSED=1 # block if adapter checks timeout/fail
Edit .uplift/dev-discipline/config:
TDD_MODE=0
RARE_COMMITS_MAX_FILES=5
RARE_COMMITS_MAX_INSERTIONS=100
RARE_COMMITS_MODE=warn
WHITELIST_PATTERNS=
DEAD_BRANCH_PROTECTED=main,master
SECRET_SCAN_ENABLED=1
SECRET_SCAN_SKIP_PATHS=tests/,fixtures/,__mocks__/
TODO_DEBT_THRESHOLD=5
TODO_DEBT_PATTERNS=TODO,FIXME,HACK,XXX
MAIN_BRANCH_COMMIT_GUARD=1
Environment variables override config values. Prefix: DEV_DISCIPLINE_.
DEV_DISCIPLINE_DISABLED=1
DEV_DISCIPLINE_DISABLE_COMMIT_CHECKS=1
CI=true
npm run build
npm testThe fixture suite keeps the existing tp-* / tn-* JSON cases and exercises both direct guard behavior and OpenCode adapter behavior.
- Node.js 20+
- npm
- git
- OpenCode for live integration
MIT