@ksm0709/context
v1.21.0
Published
Quality gate enforcement plugin for AI coding agents. Ensures every turn produces verified, committed, scope-checked work by requiring smoke checks and signal files before a turn can be marked complete.
Readme
@ksm0709/context
Quality gate enforcement plugin for AI coding agents. Ensures every turn produces verified, committed, scope-checked work by requiring smoke checks and signal files before a turn can be marked complete.
Works with Claude Code, Codex, and OpenCode using their native integration surfaces.
How It Works
Agent writes code
│
▼
Turn ends → stop hook injects "call submit_turn_complete" reminder
│
▼
Agent runs smoke checks → signal files created (.context/.check-*-passed)
│
▼
Agent calls submit_turn_complete → validates all signals are fresh
│
▼
✓ Turn marked complete (.context/.work-complete)Custom smoke checks — configurable shell commands or agent prompts (e.g., npm test, npm run lint)
Installation
Prerequisites
- Bun runtime
- Claude Code, Codex, or OpenCode
Install the package
npm install -g @ksm0709/contextRecommended
context updateThis will:
- Scaffold
.context/directory in your project - Register
context-mcpas a native MCP server in~/.config/opencode/opencode.json - Register
context-mcpMCP server via Claude CLI - Register Claude SessionStart and Stop hooks
- Install native Codex hooks to
~/.codex/hooks/and registercontext-mcp - Inject workflow context into global instructions where needed
Individual installers
context update claude
context update codexUse these only when you want to reinstall a single integration target.
Configuration
Edit .context/config.jsonc in your project root:
{
// Smoke checks define the commands that produce signal files
"smokeChecks": [
{
"name": "tests",
"command": "npm test",
"signal": ".context/.check-tests-passed"
},
{
"name": "lint",
"command": "npm run lint",
"signal": ".context/.check-lint-passed",
"enabled": false // disabled by default — enable when ready
}
]
}The scaffold template includes 13 example smoke checks (all disabled by default). Enable the ones relevant to your project.
Auto-inference
If you don't want to configure checks manually, the agent can call infer_smoke_checks() to analyze your project and auto-generate the config.
Conditional Smoke Checks
Use triggerCommand to run a smoke check only when a condition is met:
{
"smokeChecks": [
{
"name": "lint",
"command": "clang-tidy src/*.cpp",
"signal": ".context/.check-lint-passed",
// Only run when .cpp files were changed
"triggerCommand": "git diff --name-only HEAD~1 | grep -qE '\\.cpp$'"
},
{
"name": "tests",
"command": "npm test",
"signal": ".context/.check-tests-passed"
// No triggerCommand → always runs
}
]
}How triggerCommand works:
- Exit 0 → condition met, smoke check runs normally
- Non-zero exit → condition not met, auto-skip signal written (
skipped=true) - Timeout (10s) → returns error (distinct from skip)
- Not set → check always runs (backward compatible)
Checks with triggerCommand bypass the global necessity gate (which auto-skips all checks when no source code changes are detected).
Agent-type Smoke Checks
Use type: "agent" with a prompt to run an LLM-based review:
{
"smokeChecks": [
{
"name": "code-review",
"type": "agent",
"prompt": "Review the recent changes for correctness and style issues.",
"signal": ".context/.check-code-review-passed",
"timeout": 300000
}
]
}SmokeCheckEntry Fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| name | string | yes | Unique identifier for this check |
| command | string | when type=command | Shell command to execute |
| type | "command" | "agent" | no | Default: "command" |
| prompt | string | when type=agent | Prompt for LLM-based review |
| signal | string | yes | Path to signal file (must start with .context/) |
| timeout | number | no | Timeout in ms (1000-600000, default: 30000) |
| triggerCommand | string | no | Condition command; exit 0 = run, non-zero = skip |
| enabled | boolean | no | Set false to skip this check entirely (default: true) |
MCP Tools
The plugin exposes these tools via the context-mcp MCP server:
| Tool | Description |
|------|-------------|
| run_smoke_check | Run a named smoke check from config |
| infer_smoke_checks | Auto-detect and configure checks for the project |
| submit_turn_complete | Validate all signals and mark turn complete |
Typical Agent Flow
1. Agent does work (writes code, makes changes)
2. Agent commits changes
3. Agent calls run_smoke_check({ name: "tests" }) → signal file created
4. Agent calls run_smoke_check({ name: "lint" }) → signal file created
5. Agent calls submit_turn_complete() → validates all signals, writes .work-completeSlash Commands
Two slash commands are available for manual quality gate control:
| Command | Description |
|---------|-------------|
| /project:cleanup | Reset quality gate — delete .work-complete and all signal files |
| /project:manual-gating | Manually run all smoke checks and call submit_turn_complete |
Use /project:cleanup when you want to force re-running quality checks in the current session. Use /project:manual-gating to trigger the full quality gate workflow without waiting for the stop hook.
Native Hooks vs Quality Gate
These two mechanisms are complementary — hooks enforce the protocol, the quality gate verifies the work.
Claude Native Hooks
Claude Code hooks (SessionStart, Stop) are OS-level processes that run at lifecycle events, independent of MCP tools:
| Aspect | Detail |
|--------|--------|
| Trigger | Lifecycle events (session start/end, tool use) |
| Mechanism | Shell process; reads JSON from stdin, writes JSON to stdout |
| Power | Can block the agent from stopping until conditions are met |
| Context injection | Can inject additionalContext into the agent's next turn |
| Scope | Always runs — agent cannot opt out |
| Configuration | .claude/settings.json (or global ~/.claude/settings.json) |
The Stop hook is the enforcement layer. When the agent tries to stop, the hook checks whether .work-complete exists and is fresh. If not, it returns decision: block — forcing the agent back to call submit_turn_complete before it can exit.
The SessionStart hook cleans up stale .work-complete from the previous session and injects workflow context into AGENTS.md / CLAUDE.md.
Quality Gate (Smoke Checks)
The quality gate is an MCP-based protocol the agent participates in voluntarily (or under hook pressure):
| Aspect | Detail |
|--------|--------|
| Trigger | Agent calls MCP tools explicitly |
| Mechanism | MCP server validates signal files with TTL + session binding |
| Power | Validates actual work quality (tests, lint, custom checks) |
| Context injection | None — only produces signal files and a .work-complete marker |
| Scope | Configurable per-project; individual checks can be disabled |
| Configuration | .context/config.jsonc |
How They Work Together
Stop hook (enforcement) Quality gate (verification)
───────────────────── ───────────────────────────
Agent tries to stop Agent calls run_smoke_check()
│ │
▼ ▼
Hook checks .work-complete Runs npm test / lint / agent prompt
│ │
missing → block pass → writes signal file
│ │
present → allow ▼
Agent calls submit_turn_complete()
│
validates signals (TTL + session)
│
writes .work-completeKey differences:
| | Native Hook | Quality Gate |
|---|---|---|
| Who runs it | OS / Claude runtime | Agent (via MCP call) |
| Can block agent | Yes (decision: block) | No (only produces files) |
| Validates work quality | No | Yes (tests, lint, review) |
| Multi-agent support | Claude-specific | Claude + Codex + OpenCode |
| Per-project config | Limited | Full (enable/disable/conditions) |
| Runs without agent cooperation | Yes | No (agent must call tools) |
In practice: the Stop hook is the "locked door" — the agent cannot exit without calling submit_turn_complete. The quality gate is the "checklist" behind the door — it defines what "done" actually means for this project.
Necessity Gate
When no source code files are changed in a session (only docs like .md, .txt, .rst), the plugin automatically generates skip signals for all checks, so the agent doesn't need to run smoke checks for documentation-only changes.
Checks with triggerCommand are excluded from this auto-skip — their triggerCommand decides independently.
CLI Commands
# Update scaffold and install Claude/Codex/OpenCode integrations
context update
# Update the plugin package itself
context update plugin
# Migrate from legacy .opencode/context/ to .context/
context update migrate
# Reinstall Claude integration only
context update claude
# Reinstall Codex integration only
context update codex
# Show version
context --versionProject Structure
.context/
├── config.jsonc # Plugin configuration (smokeChecks)
├── .version # Plugin version tracker for auto-updates
├── .check-*-passed # Signal files (auto-generated, gitignored)
└── .work-complete # Turn completion marker (auto-generated)Signal File Format
Signal files are plain text with key-value pairs:
session_id=abc123
timestamp=1712456789000
caller=agentSkip signals (from triggerCommand or necessity gate):
session_id=abc123
timestamp=1712456789000
skipped=true- TTL: 1 hour — stale signals are rejected by
submit_turn_complete - Session binding: signals from a different session are rejected
Development
# Install dependencies
bun install
# Build
npm run build
# Run tests
npm test
# Lint
npm run lintLicense
MIT
