crucible-mcp 0.5.0__tar.gz → 1.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- crucible_mcp-1.0.1/PKG-INFO +198 -0
- crucible_mcp-1.0.1/README.md +180 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/pyproject.toml +8 -1
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/cli.py +109 -2
- crucible_mcp-1.0.1/src/crucible/enforcement/bundled/error-handling.yaml +84 -0
- crucible_mcp-1.0.1/src/crucible/enforcement/bundled/security.yaml +123 -0
- crucible_mcp-1.0.1/src/crucible/enforcement/bundled/smart-contract.yaml +110 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/enforcement/compliance.py +9 -5
- crucible_mcp-1.0.1/src/crucible/hooks/claudecode.py +388 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/hooks/precommit.py +117 -25
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/knowledge/loader.py +186 -0
- crucible_mcp-1.0.1/src/crucible/knowledge/principles/API_DESIGN.md +176 -0
- crucible_mcp-1.0.1/src/crucible/knowledge/principles/COMMITS.md +127 -0
- crucible_mcp-1.0.1/src/crucible/knowledge/principles/DATABASE.md +138 -0
- crucible_mcp-1.0.1/src/crucible/knowledge/principles/DOCUMENTATION.md +201 -0
- crucible_mcp-1.0.1/src/crucible/knowledge/principles/ERROR_HANDLING.md +157 -0
- crucible_mcp-1.0.1/src/crucible/knowledge/principles/FP.md +162 -0
- crucible_mcp-1.0.1/src/crucible/knowledge/principles/GITIGNORE.md +218 -0
- crucible_mcp-1.0.1/src/crucible/knowledge/principles/OBSERVABILITY.md +147 -0
- crucible_mcp-1.0.1/src/crucible/knowledge/principles/PRECOMMIT.md +201 -0
- crucible_mcp-1.0.1/src/crucible/knowledge/principles/SECURITY.md +136 -0
- crucible_mcp-1.0.1/src/crucible/knowledge/principles/SMART_CONTRACT.md +153 -0
- crucible_mcp-1.0.1/src/crucible/knowledge/principles/SYSTEM_DESIGN.md +153 -0
- crucible_mcp-1.0.1/src/crucible/knowledge/principles/TESTING.md +129 -0
- crucible_mcp-1.0.1/src/crucible/knowledge/principles/TYPE_SAFETY.md +170 -0
- crucible_mcp-1.0.1/src/crucible/skills/accessibility-engineer/SKILL.md +71 -0
- crucible_mcp-1.0.1/src/crucible/skills/backend-engineer/SKILL.md +69 -0
- crucible_mcp-1.0.1/src/crucible/skills/customer-success/SKILL.md +69 -0
- crucible_mcp-1.0.1/src/crucible/skills/data-engineer/SKILL.md +70 -0
- crucible_mcp-1.0.1/src/crucible/skills/devops-engineer/SKILL.md +69 -0
- crucible_mcp-1.0.1/src/crucible/skills/fde-engineer/SKILL.md +69 -0
- crucible_mcp-1.0.1/src/crucible/skills/formal-verification/SKILL.md +86 -0
- crucible_mcp-1.0.1/src/crucible/skills/gas-optimizer/SKILL.md +89 -0
- crucible_mcp-1.0.1/src/crucible/skills/incident-responder/SKILL.md +91 -0
- crucible_mcp-1.0.1/src/crucible/skills/mev-researcher/SKILL.md +87 -0
- crucible_mcp-1.0.1/src/crucible/skills/mobile-engineer/SKILL.md +70 -0
- crucible_mcp-1.0.1/src/crucible/skills/performance-engineer/SKILL.md +68 -0
- crucible_mcp-1.0.1/src/crucible/skills/product-engineer/SKILL.md +68 -0
- crucible_mcp-1.0.1/src/crucible/skills/protocol-architect/SKILL.md +83 -0
- crucible_mcp-1.0.1/src/crucible/skills/security-engineer/SKILL.md +63 -0
- crucible_mcp-1.0.1/src/crucible/skills/tech-lead/SKILL.md +92 -0
- crucible_mcp-1.0.1/src/crucible/skills/uiux-engineer/SKILL.md +70 -0
- crucible_mcp-1.0.1/src/crucible/skills/web3-engineer/SKILL.md +79 -0
- crucible_mcp-1.0.1/src/crucible_mcp.egg-info/PKG-INFO +198 -0
- crucible_mcp-1.0.1/src/crucible_mcp.egg-info/SOURCES.txt +83 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/tests/test_cli.py +5 -5
- crucible_mcp-0.5.0/PKG-INFO +0 -161
- crucible_mcp-0.5.0/README.md +0 -143
- crucible_mcp-0.5.0/src/crucible_mcp.egg-info/PKG-INFO +0 -161
- crucible_mcp-0.5.0/src/crucible_mcp.egg-info/SOURCES.txt +0 -47
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/setup.cfg +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/__init__.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/domain/__init__.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/domain/detection.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/enforcement/__init__.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/enforcement/assertions.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/enforcement/budget.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/enforcement/models.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/enforcement/patterns.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/errors.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/hooks/__init__.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/knowledge/__init__.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/models.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/review/__init__.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/review/core.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/server.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/skills/__init__.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/skills/loader.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/synthesis/__init__.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/tools/__init__.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/tools/delegation.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible/tools/git.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible_mcp.egg-info/dependency_links.txt +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible_mcp.egg-info/entry_points.txt +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible_mcp.egg-info/requires.txt +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/src/crucible_mcp.egg-info/top_level.txt +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/tests/test_compliance.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/tests/test_detection.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/tests/test_enforcement.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/tests/test_full_review.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/tests/test_git.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/tests/test_hooks_cli.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/tests/test_integration.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/tests/test_knowledge.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/tests/test_precommit.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/tests/test_server.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/tests/test_skills.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/tests/test_skills_loader.py +0 -0
- {crucible_mcp-0.5.0 → crucible_mcp-1.0.1}/tests/test_tools.py +0 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: crucible-mcp
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Code review MCP server for Claude. Not affiliated with Atlassian.
|
|
5
|
+
Author: be.nvy
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: mcp,code-review,static-analysis,claude
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
Requires-Dist: mcp>=1.0.0
|
|
11
|
+
Requires-Dist: pyyaml>=6.0
|
|
12
|
+
Requires-Dist: anthropic>=0.40.0
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
16
|
+
Requires-Dist: mypy>=1.8; extra == "dev"
|
|
17
|
+
Requires-Dist: ruff>=0.3; extra == "dev"
|
|
18
|
+
|
|
19
|
+
# Crucible
|
|
20
|
+
|
|
21
|
+
**Your team's standards, applied by Claude, every time.**
|
|
22
|
+
|
|
23
|
+
Claude without context applies generic best practices. Crucible loads *your* patterns—so Claude reviews code the way your team would, not the way the internet would.
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
├── Enforcement: Pattern + LLM assertions that block bad code
|
|
27
|
+
├── Personas: Domain-specific thinking (how to approach problems)
|
|
28
|
+
├── Knowledge: Coding patterns and principles (what to apply)
|
|
29
|
+
├── Cascade: Project → User → Bundled (customizable at every level)
|
|
30
|
+
└── Context-aware: Loads relevant skills based on what you're working on
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Why Crucible?**
|
|
34
|
+
- **Enforcement** — Not suggestions, constraints. Assertions block code that violates your patterns
|
|
35
|
+
- **Consistency** — Same checklist every time, for every engineer, every session
|
|
36
|
+
- **Automation** — Runs in CI, pre-commit hooks, and Claude Code hooks
|
|
37
|
+
- **Institutional knowledge** — Your senior engineer's mental checklist, in the repo
|
|
38
|
+
- **Your context** — Security fundamentals plus *your* auth patterns, *your* conventions
|
|
39
|
+
- **Cost efficiency** — Filter with free tools first, LLM only on what needs judgment
|
|
40
|
+
|
|
41
|
+
> Not affiliated with Atlassian's Crucible.
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install crucible-mcp
|
|
47
|
+
|
|
48
|
+
# Initialize your project
|
|
49
|
+
crucible init --with-claudemd
|
|
50
|
+
|
|
51
|
+
# Install enforcement hooks
|
|
52
|
+
crucible hooks install # Git pre-commit
|
|
53
|
+
crucible hooks claudecode init # Claude Code hooks
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
That's it. Crucible will now:
|
|
57
|
+
1. Run on every commit (pre-commit hook)
|
|
58
|
+
2. Review files Claude edits (Claude Code hook)
|
|
59
|
+
3. Block code that violates bundled assertions (security, error handling, smart contracts)
|
|
60
|
+
|
|
61
|
+
## How Enforcement Works
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
Claude writes code
|
|
65
|
+
↓
|
|
66
|
+
PostToolUse hook triggers
|
|
67
|
+
↓
|
|
68
|
+
Crucible runs pattern assertions
|
|
69
|
+
↓
|
|
70
|
+
Finding detected → Exit 2 (block) + feedback to Claude
|
|
71
|
+
↓
|
|
72
|
+
Claude fixes the issue
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**30 bundled assertions** covering:
|
|
76
|
+
- Security: eval, exec, shell injection, pickle, hardcoded secrets, SQL injection
|
|
77
|
+
- Error handling: bare except, silent catch, empty catch blocks
|
|
78
|
+
- Smart contracts: reentrancy, CEI violations, access control, tx.origin auth
|
|
79
|
+
|
|
80
|
+
**Customize with your own assertions** in `.crucible/assertions/`:
|
|
81
|
+
|
|
82
|
+
```yaml
|
|
83
|
+
# .crucible/assertions/my-rules.yaml
|
|
84
|
+
version: "1.0"
|
|
85
|
+
name: my-rules
|
|
86
|
+
assertions:
|
|
87
|
+
- id: no-console-log
|
|
88
|
+
type: pattern
|
|
89
|
+
pattern: "console\\.log\\("
|
|
90
|
+
message: "Remove console.log before committing"
|
|
91
|
+
severity: warning
|
|
92
|
+
priority: medium
|
|
93
|
+
languages: [javascript, typescript]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## MCP Tools
|
|
97
|
+
|
|
98
|
+
Add to Claude Code (`.mcp.json`):
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"mcpServers": {
|
|
103
|
+
"crucible": {
|
|
104
|
+
"command": "crucible-mcp"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
| Tool | Purpose |
|
|
111
|
+
|------|---------|
|
|
112
|
+
| `review(path)` | Full review: analysis + skills + knowledge + assertions |
|
|
113
|
+
| `review(mode='staged')` | Review git changes with enforcement |
|
|
114
|
+
| `load_knowledge(files)` | Load specific knowledge files |
|
|
115
|
+
| `get_principles(topic)` | Load engineering knowledge by topic |
|
|
116
|
+
| `delegate_*` | Direct tool access (semgrep, ruff, slither, bandit) |
|
|
117
|
+
| `check_tools()` | Show installed analysis tools |
|
|
118
|
+
|
|
119
|
+
## CLI
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
# Review
|
|
123
|
+
crucible review # Review staged changes
|
|
124
|
+
crucible review --mode branch # Review current branch vs main
|
|
125
|
+
crucible review src/file.py --no-git # Review without git
|
|
126
|
+
|
|
127
|
+
# Assertions
|
|
128
|
+
crucible assertions list # List all assertion files
|
|
129
|
+
crucible assertions test file.py # Test assertions against a file
|
|
130
|
+
|
|
131
|
+
# Hooks
|
|
132
|
+
crucible hooks install # Install pre-commit hook
|
|
133
|
+
crucible hooks claudecode init # Initialize Claude Code hooks
|
|
134
|
+
|
|
135
|
+
# Customize
|
|
136
|
+
crucible skills init <skill> # Copy skill for customization
|
|
137
|
+
crucible knowledge init <file> # Copy knowledge for customization
|
|
138
|
+
|
|
139
|
+
# CI
|
|
140
|
+
crucible ci generate # Generate GitHub Actions workflow
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Customization
|
|
144
|
+
|
|
145
|
+
Everything follows cascade resolution (first found wins):
|
|
146
|
+
1. `.crucible/` — Project overrides (checked into repo)
|
|
147
|
+
2. `~/.claude/crucible/` — User preferences
|
|
148
|
+
3. Bundled — Package defaults
|
|
149
|
+
|
|
150
|
+
**Override a skill:**
|
|
151
|
+
```bash
|
|
152
|
+
crucible skills init security-engineer
|
|
153
|
+
# Edit .crucible/skills/security-engineer/SKILL.md
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Add project knowledge:**
|
|
157
|
+
```bash
|
|
158
|
+
crucible knowledge init SECURITY
|
|
159
|
+
# Edit .crucible/knowledge/SECURITY.md
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
**Add custom assertions:**
|
|
163
|
+
```bash
|
|
164
|
+
mkdir -p .crucible/assertions
|
|
165
|
+
# Create .crucible/assertions/my-rules.yaml
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
See [CUSTOMIZATION.md](docs/CUSTOMIZATION.md) for the full guide.
|
|
169
|
+
|
|
170
|
+
## What's Included
|
|
171
|
+
|
|
172
|
+
**30 Bundled Assertions** — Pattern rules for security, error handling, and smart contracts.
|
|
173
|
+
|
|
174
|
+
**18 Personas** — Domain-specific thinking: security, performance, accessibility, web3, backend, and more.
|
|
175
|
+
|
|
176
|
+
**14 Knowledge Files** — Coding patterns and principles for security, testing, APIs, databases, smart contracts, etc.
|
|
177
|
+
|
|
178
|
+
See [SKILLS.md](docs/SKILLS.md) and [KNOWLEDGE.md](docs/KNOWLEDGE.md) for details.
|
|
179
|
+
|
|
180
|
+
## Documentation
|
|
181
|
+
|
|
182
|
+
| Doc | What's In It |
|
|
183
|
+
|-----|--------------|
|
|
184
|
+
| [QUICKSTART.md](docs/QUICKSTART.md) | 5-minute setup guide |
|
|
185
|
+
| [FEATURES.md](docs/FEATURES.md) | Complete feature reference |
|
|
186
|
+
| [ARCHITECTURE.md](docs/ARCHITECTURE.md) | How MCP, tools, skills, and knowledge fit together |
|
|
187
|
+
| [CUSTOMIZATION.md](docs/CUSTOMIZATION.md) | Override skills and knowledge for your project |
|
|
188
|
+
| [SKILLS.md](docs/SKILLS.md) | All 18 personas with triggers and focus areas |
|
|
189
|
+
| [KNOWLEDGE.md](docs/KNOWLEDGE.md) | All 14 knowledge files with topics covered |
|
|
190
|
+
| [CONTRIBUTING.md](docs/CONTRIBUTING.md) | Adding tools, skills, and knowledge |
|
|
191
|
+
|
|
192
|
+
## Development
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
pip install -e ".[dev]"
|
|
196
|
+
pytest # Run tests (580+ tests)
|
|
197
|
+
ruff check src/ --fix # Lint
|
|
198
|
+
```
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Crucible
|
|
2
|
+
|
|
3
|
+
**Your team's standards, applied by Claude, every time.**
|
|
4
|
+
|
|
5
|
+
Claude without context applies generic best practices. Crucible loads *your* patterns—so Claude reviews code the way your team would, not the way the internet would.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
├── Enforcement: Pattern + LLM assertions that block bad code
|
|
9
|
+
├── Personas: Domain-specific thinking (how to approach problems)
|
|
10
|
+
├── Knowledge: Coding patterns and principles (what to apply)
|
|
11
|
+
├── Cascade: Project → User → Bundled (customizable at every level)
|
|
12
|
+
└── Context-aware: Loads relevant skills based on what you're working on
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
**Why Crucible?**
|
|
16
|
+
- **Enforcement** — Not suggestions, constraints. Assertions block code that violates your patterns
|
|
17
|
+
- **Consistency** — Same checklist every time, for every engineer, every session
|
|
18
|
+
- **Automation** — Runs in CI, pre-commit hooks, and Claude Code hooks
|
|
19
|
+
- **Institutional knowledge** — Your senior engineer's mental checklist, in the repo
|
|
20
|
+
- **Your context** — Security fundamentals plus *your* auth patterns, *your* conventions
|
|
21
|
+
- **Cost efficiency** — Filter with free tools first, LLM only on what needs judgment
|
|
22
|
+
|
|
23
|
+
> Not affiliated with Atlassian's Crucible.
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install crucible-mcp
|
|
29
|
+
|
|
30
|
+
# Initialize your project
|
|
31
|
+
crucible init --with-claudemd
|
|
32
|
+
|
|
33
|
+
# Install enforcement hooks
|
|
34
|
+
crucible hooks install # Git pre-commit
|
|
35
|
+
crucible hooks claudecode init # Claude Code hooks
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
That's it. Crucible will now:
|
|
39
|
+
1. Run on every commit (pre-commit hook)
|
|
40
|
+
2. Review files Claude edits (Claude Code hook)
|
|
41
|
+
3. Block code that violates bundled assertions (security, error handling, smart contracts)
|
|
42
|
+
|
|
43
|
+
## How Enforcement Works
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
Claude writes code
|
|
47
|
+
↓
|
|
48
|
+
PostToolUse hook triggers
|
|
49
|
+
↓
|
|
50
|
+
Crucible runs pattern assertions
|
|
51
|
+
↓
|
|
52
|
+
Finding detected → Exit 2 (block) + feedback to Claude
|
|
53
|
+
↓
|
|
54
|
+
Claude fixes the issue
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**30 bundled assertions** covering:
|
|
58
|
+
- Security: eval, exec, shell injection, pickle, hardcoded secrets, SQL injection
|
|
59
|
+
- Error handling: bare except, silent catch, empty catch blocks
|
|
60
|
+
- Smart contracts: reentrancy, CEI violations, access control, tx.origin auth
|
|
61
|
+
|
|
62
|
+
**Customize with your own assertions** in `.crucible/assertions/`:
|
|
63
|
+
|
|
64
|
+
```yaml
|
|
65
|
+
# .crucible/assertions/my-rules.yaml
|
|
66
|
+
version: "1.0"
|
|
67
|
+
name: my-rules
|
|
68
|
+
assertions:
|
|
69
|
+
- id: no-console-log
|
|
70
|
+
type: pattern
|
|
71
|
+
pattern: "console\\.log\\("
|
|
72
|
+
message: "Remove console.log before committing"
|
|
73
|
+
severity: warning
|
|
74
|
+
priority: medium
|
|
75
|
+
languages: [javascript, typescript]
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## MCP Tools
|
|
79
|
+
|
|
80
|
+
Add to Claude Code (`.mcp.json`):
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"mcpServers": {
|
|
85
|
+
"crucible": {
|
|
86
|
+
"command": "crucible-mcp"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
| Tool | Purpose |
|
|
93
|
+
|------|---------|
|
|
94
|
+
| `review(path)` | Full review: analysis + skills + knowledge + assertions |
|
|
95
|
+
| `review(mode='staged')` | Review git changes with enforcement |
|
|
96
|
+
| `load_knowledge(files)` | Load specific knowledge files |
|
|
97
|
+
| `get_principles(topic)` | Load engineering knowledge by topic |
|
|
98
|
+
| `delegate_*` | Direct tool access (semgrep, ruff, slither, bandit) |
|
|
99
|
+
| `check_tools()` | Show installed analysis tools |
|
|
100
|
+
|
|
101
|
+
## CLI
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
# Review
|
|
105
|
+
crucible review # Review staged changes
|
|
106
|
+
crucible review --mode branch # Review current branch vs main
|
|
107
|
+
crucible review src/file.py --no-git # Review without git
|
|
108
|
+
|
|
109
|
+
# Assertions
|
|
110
|
+
crucible assertions list # List all assertion files
|
|
111
|
+
crucible assertions test file.py # Test assertions against a file
|
|
112
|
+
|
|
113
|
+
# Hooks
|
|
114
|
+
crucible hooks install # Install pre-commit hook
|
|
115
|
+
crucible hooks claudecode init # Initialize Claude Code hooks
|
|
116
|
+
|
|
117
|
+
# Customize
|
|
118
|
+
crucible skills init <skill> # Copy skill for customization
|
|
119
|
+
crucible knowledge init <file> # Copy knowledge for customization
|
|
120
|
+
|
|
121
|
+
# CI
|
|
122
|
+
crucible ci generate # Generate GitHub Actions workflow
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Customization
|
|
126
|
+
|
|
127
|
+
Everything follows cascade resolution (first found wins):
|
|
128
|
+
1. `.crucible/` — Project overrides (checked into repo)
|
|
129
|
+
2. `~/.claude/crucible/` — User preferences
|
|
130
|
+
3. Bundled — Package defaults
|
|
131
|
+
|
|
132
|
+
**Override a skill:**
|
|
133
|
+
```bash
|
|
134
|
+
crucible skills init security-engineer
|
|
135
|
+
# Edit .crucible/skills/security-engineer/SKILL.md
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Add project knowledge:**
|
|
139
|
+
```bash
|
|
140
|
+
crucible knowledge init SECURITY
|
|
141
|
+
# Edit .crucible/knowledge/SECURITY.md
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Add custom assertions:**
|
|
145
|
+
```bash
|
|
146
|
+
mkdir -p .crucible/assertions
|
|
147
|
+
# Create .crucible/assertions/my-rules.yaml
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
See [CUSTOMIZATION.md](docs/CUSTOMIZATION.md) for the full guide.
|
|
151
|
+
|
|
152
|
+
## What's Included
|
|
153
|
+
|
|
154
|
+
**30 Bundled Assertions** — Pattern rules for security, error handling, and smart contracts.
|
|
155
|
+
|
|
156
|
+
**18 Personas** — Domain-specific thinking: security, performance, accessibility, web3, backend, and more.
|
|
157
|
+
|
|
158
|
+
**14 Knowledge Files** — Coding patterns and principles for security, testing, APIs, databases, smart contracts, etc.
|
|
159
|
+
|
|
160
|
+
See [SKILLS.md](docs/SKILLS.md) and [KNOWLEDGE.md](docs/KNOWLEDGE.md) for details.
|
|
161
|
+
|
|
162
|
+
## Documentation
|
|
163
|
+
|
|
164
|
+
| Doc | What's In It |
|
|
165
|
+
|-----|--------------|
|
|
166
|
+
| [QUICKSTART.md](docs/QUICKSTART.md) | 5-minute setup guide |
|
|
167
|
+
| [FEATURES.md](docs/FEATURES.md) | Complete feature reference |
|
|
168
|
+
| [ARCHITECTURE.md](docs/ARCHITECTURE.md) | How MCP, tools, skills, and knowledge fit together |
|
|
169
|
+
| [CUSTOMIZATION.md](docs/CUSTOMIZATION.md) | Override skills and knowledge for your project |
|
|
170
|
+
| [SKILLS.md](docs/SKILLS.md) | All 18 personas with triggers and focus areas |
|
|
171
|
+
| [KNOWLEDGE.md](docs/KNOWLEDGE.md) | All 14 knowledge files with topics covered |
|
|
172
|
+
| [CONTRIBUTING.md](docs/CONTRIBUTING.md) | Adding tools, skills, and knowledge |
|
|
173
|
+
|
|
174
|
+
## Development
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
pip install -e ".[dev]"
|
|
178
|
+
pytest # Run tests (580+ tests)
|
|
179
|
+
ruff check src/ --fix # Lint
|
|
180
|
+
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "crucible-mcp"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "1.0.1"
|
|
4
4
|
description = "Code review MCP server for Claude. Not affiliated with Atlassian."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -33,6 +33,13 @@ build-backend = "setuptools.build_meta"
|
|
|
33
33
|
[tool.setuptools.packages.find]
|
|
34
34
|
where = ["src"]
|
|
35
35
|
|
|
36
|
+
[tool.setuptools.package-data]
|
|
37
|
+
crucible = [
|
|
38
|
+
"skills/**/*.md",
|
|
39
|
+
"knowledge/principles/*.md",
|
|
40
|
+
"enforcement/bundled/*.yaml",
|
|
41
|
+
]
|
|
42
|
+
|
|
36
43
|
[tool.ruff]
|
|
37
44
|
line-length = 100
|
|
38
45
|
target-version = "py311"
|
|
@@ -578,6 +578,27 @@ def _cmd_review_no_git(args: argparse.Namespace, path: str) -> int:
|
|
|
578
578
|
# Deduplicate
|
|
579
579
|
all_findings = deduplicate_findings(all_findings)
|
|
580
580
|
|
|
581
|
+
# Run enforcement assertions
|
|
582
|
+
from crucible.review.core import run_enforcement
|
|
583
|
+
|
|
584
|
+
compliance_config = _build_compliance_config(
|
|
585
|
+
config,
|
|
586
|
+
cli_token_budget=getattr(args, "token_budget", None),
|
|
587
|
+
cli_model=getattr(args, "compliance_model", None),
|
|
588
|
+
cli_no_compliance=getattr(args, "no_compliance", False),
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
# Use current directory as repo root for enforcement
|
|
592
|
+
enforcement_findings, enforcement_errors, assertions_checked, assertions_skipped, budget_state = (
|
|
593
|
+
run_enforcement(
|
|
594
|
+
".",
|
|
595
|
+
changed_files=files_to_analyze,
|
|
596
|
+
repo_root=".",
|
|
597
|
+
compliance_config=compliance_config,
|
|
598
|
+
)
|
|
599
|
+
)
|
|
600
|
+
tool_errors.extend(enforcement_errors)
|
|
601
|
+
|
|
581
602
|
# Compute severity summary
|
|
582
603
|
severity_counts = compute_severity_counts(all_findings)
|
|
583
604
|
|
|
@@ -607,6 +628,21 @@ def _cmd_review_no_git(args: argparse.Namespace, path: str) -> int:
|
|
|
607
628
|
}
|
|
608
629
|
for f in all_findings
|
|
609
630
|
],
|
|
631
|
+
"enforcement": {
|
|
632
|
+
"findings": [
|
|
633
|
+
{
|
|
634
|
+
"assertion_id": f.assertion_id,
|
|
635
|
+
"severity": f.severity,
|
|
636
|
+
"message": f.message,
|
|
637
|
+
"location": f.location,
|
|
638
|
+
"source": f.source,
|
|
639
|
+
}
|
|
640
|
+
for f in enforcement_findings
|
|
641
|
+
],
|
|
642
|
+
"assertions_checked": assertions_checked,
|
|
643
|
+
"assertions_skipped": assertions_skipped,
|
|
644
|
+
"tokens_used": budget_state.tokens_used if budget_state else 0,
|
|
645
|
+
},
|
|
610
646
|
"severity_counts": severity_counts,
|
|
611
647
|
"passed": passed,
|
|
612
648
|
"threshold": default_threshold.value if default_threshold else None,
|
|
@@ -616,7 +652,7 @@ def _cmd_review_no_git(args: argparse.Namespace, path: str) -> int:
|
|
|
616
652
|
else:
|
|
617
653
|
# Text output
|
|
618
654
|
if all_findings:
|
|
619
|
-
print(f"\nFound {len(all_findings)} issue(s):\n")
|
|
655
|
+
print(f"\nFound {len(all_findings)} static analysis issue(s):\n")
|
|
620
656
|
for f in all_findings:
|
|
621
657
|
sev_icon = {"critical": "🔴", "high": "🟠", "medium": "🟡", "low": "🔵", "info": "⚪"}.get(
|
|
622
658
|
f.severity.value, "⚪"
|
|
@@ -626,7 +662,18 @@ def _cmd_review_no_git(args: argparse.Namespace, path: str) -> int:
|
|
|
626
662
|
if f.suggestion:
|
|
627
663
|
print(f" 💡 {f.suggestion}")
|
|
628
664
|
print()
|
|
629
|
-
|
|
665
|
+
|
|
666
|
+
# Enforcement findings
|
|
667
|
+
if enforcement_findings:
|
|
668
|
+
print(f"\nEnforcement Assertions ({len(enforcement_findings)}):")
|
|
669
|
+
for f in enforcement_findings:
|
|
670
|
+
sev_icon = {"error": "🔴", "warning": "🟠", "info": "⚪"}.get(f.severity, "⚪")
|
|
671
|
+
source_tag = "[LLM]" if f.source == "llm" else "[Pattern]"
|
|
672
|
+
print(f" {sev_icon} [{f.severity.upper()}] {source_tag} {f.assertion_id}: {f.location}")
|
|
673
|
+
print(f" {f.message}")
|
|
674
|
+
print()
|
|
675
|
+
|
|
676
|
+
if not all_findings and not enforcement_findings:
|
|
630
677
|
print("\n✅ No issues found.")
|
|
631
678
|
|
|
632
679
|
# Summary
|
|
@@ -634,6 +681,11 @@ def _cmd_review_no_git(args: argparse.Namespace, path: str) -> int:
|
|
|
634
681
|
counts_str = ", ".join(f"{k}: {v}" for k, v in severity_counts.items() if v > 0)
|
|
635
682
|
print(f"Summary: {counts_str}")
|
|
636
683
|
|
|
684
|
+
if assertions_checked or assertions_skipped:
|
|
685
|
+
print(f"Assertions: {assertions_checked} checked, {assertions_skipped} skipped")
|
|
686
|
+
if budget_state and budget_state.tokens_used > 0:
|
|
687
|
+
print(f" LLM tokens used: {budget_state.tokens_used}")
|
|
688
|
+
|
|
637
689
|
if tool_errors and not args.quiet:
|
|
638
690
|
print(f"\n⚠️ {len(tool_errors)} tool error(s)")
|
|
639
691
|
for err in tool_errors[:5]:
|
|
@@ -1682,11 +1734,30 @@ include_context: false
|
|
|
1682
1734
|
if not gitignore_path.exists():
|
|
1683
1735
|
gitignore_path.write_text("# Local overrides (optional)\n*.local.md\n")
|
|
1684
1736
|
|
|
1737
|
+
# Create minimal CLAUDE.md if requested
|
|
1738
|
+
if args.with_claudemd:
|
|
1739
|
+
claudemd_path = project_path / "CLAUDE.md"
|
|
1740
|
+
if claudemd_path.exists() and not args.force:
|
|
1741
|
+
print(f"Warning: {claudemd_path} exists, skipping (use --force to overwrite)")
|
|
1742
|
+
else:
|
|
1743
|
+
project_name = project_path.name
|
|
1744
|
+
claudemd_content = f"""# {project_name}
|
|
1745
|
+
|
|
1746
|
+
Use Crucible for code review: `crucible review`
|
|
1747
|
+
|
|
1748
|
+
For full engineering principles and patterns, run:
|
|
1749
|
+
- `crucible knowledge list` - see available knowledge
|
|
1750
|
+
- `crucible skills list` - see available review personas
|
|
1751
|
+
"""
|
|
1752
|
+
claudemd_path.write_text(claudemd_content)
|
|
1753
|
+
print(f"Created {claudemd_path}")
|
|
1754
|
+
|
|
1685
1755
|
print(f"\nInitialized {crucible_dir}")
|
|
1686
1756
|
print("\nNext steps:")
|
|
1687
1757
|
print(" 1. Customize skills: crucible skills init <skill>")
|
|
1688
1758
|
print(" 2. Customize knowledge: crucible knowledge init <file>")
|
|
1689
1759
|
print(" 3. Install git hooks: crucible hooks install")
|
|
1760
|
+
print(" 4. Claude Code hooks: crucible hooks claudecode init")
|
|
1690
1761
|
return 0
|
|
1691
1762
|
|
|
1692
1763
|
|
|
@@ -1950,6 +2021,28 @@ def main() -> int:
|
|
|
1950
2021
|
"path", nargs="?", default=".", help="Repository path"
|
|
1951
2022
|
)
|
|
1952
2023
|
|
|
2024
|
+
# hooks claudecode
|
|
2025
|
+
hooks_claudecode_parser = hooks_sub.add_parser(
|
|
2026
|
+
"claudecode",
|
|
2027
|
+
help="Claude Code hooks integration"
|
|
2028
|
+
)
|
|
2029
|
+
hooks_claudecode_sub = hooks_claudecode_parser.add_subparsers(dest="claudecode_command")
|
|
2030
|
+
|
|
2031
|
+
# hooks claudecode init
|
|
2032
|
+
hooks_cc_init_parser = hooks_claudecode_sub.add_parser(
|
|
2033
|
+
"init",
|
|
2034
|
+
help="Initialize Claude Code hooks for project"
|
|
2035
|
+
)
|
|
2036
|
+
hooks_cc_init_parser.add_argument(
|
|
2037
|
+
"path", nargs="?", default=".", help="Project path"
|
|
2038
|
+
)
|
|
2039
|
+
|
|
2040
|
+
# hooks claudecode hook (called by Claude Code)
|
|
2041
|
+
hooks_claudecode_sub.add_parser(
|
|
2042
|
+
"hook",
|
|
2043
|
+
help="Run hook (reads JSON from stdin)"
|
|
2044
|
+
)
|
|
2045
|
+
|
|
1953
2046
|
# === review command ===
|
|
1954
2047
|
review_parser = subparsers.add_parser(
|
|
1955
2048
|
"review",
|
|
@@ -2044,6 +2137,10 @@ def main() -> int:
|
|
|
2044
2137
|
"--minimal", action="store_true",
|
|
2045
2138
|
help="Create minimal config without copying skills"
|
|
2046
2139
|
)
|
|
2140
|
+
init_proj_parser.add_argument(
|
|
2141
|
+
"--with-claudemd", action="store_true",
|
|
2142
|
+
help="Generate minimal CLAUDE.md that points to Crucible"
|
|
2143
|
+
)
|
|
2047
2144
|
init_proj_parser.add_argument(
|
|
2048
2145
|
"path", nargs="?", default=".",
|
|
2049
2146
|
help="Project path (default: current directory)"
|
|
@@ -2161,6 +2258,15 @@ def main() -> int:
|
|
|
2161
2258
|
return cmd_hooks_uninstall(args)
|
|
2162
2259
|
elif args.hooks_command == "status":
|
|
2163
2260
|
return cmd_hooks_status(args)
|
|
2261
|
+
elif args.hooks_command == "claudecode":
|
|
2262
|
+
from crucible.hooks.claudecode import main_init, run_hook
|
|
2263
|
+
if args.claudecode_command == "init":
|
|
2264
|
+
return main_init(args.path)
|
|
2265
|
+
elif args.claudecode_command == "hook":
|
|
2266
|
+
return run_hook()
|
|
2267
|
+
else:
|
|
2268
|
+
hooks_claudecode_parser.print_help()
|
|
2269
|
+
return 0
|
|
2164
2270
|
else:
|
|
2165
2271
|
hooks_parser.print_help()
|
|
2166
2272
|
return 0
|
|
@@ -2211,6 +2317,7 @@ def main() -> int:
|
|
|
2211
2317
|
print(" crucible hooks install Install pre-commit hook to .git/hooks/")
|
|
2212
2318
|
print(" crucible hooks uninstall Remove pre-commit hook")
|
|
2213
2319
|
print(" crucible hooks status Show hook installation status")
|
|
2320
|
+
print(" crucible hooks claudecode init Initialize Claude Code hooks")
|
|
2214
2321
|
print()
|
|
2215
2322
|
print(" crucible assertions list List assertion files from all sources")
|
|
2216
2323
|
print(" crucible assertions validate Validate assertion files")
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
version: "1.0"
|
|
2
|
+
name: error-handling
|
|
3
|
+
description: Error handling best practices to prevent silent failures
|
|
4
|
+
linked_knowledge: ERROR_HANDLING.md
|
|
5
|
+
|
|
6
|
+
assertions:
|
|
7
|
+
# High: Silent failures
|
|
8
|
+
- id: no-bare-except
|
|
9
|
+
type: pattern
|
|
10
|
+
pattern: "except\\s*:"
|
|
11
|
+
message: "Bare except catches everything including SystemExit/KeyboardInterrupt - catch specific exceptions"
|
|
12
|
+
severity: warning
|
|
13
|
+
priority: high
|
|
14
|
+
languages: [python]
|
|
15
|
+
|
|
16
|
+
- id: no-pass-in-except
|
|
17
|
+
type: pattern
|
|
18
|
+
pattern: "except[^:]*:\\s*\\n\\s*pass\\s*$"
|
|
19
|
+
message: "Empty except block silently swallows errors - log or re-raise"
|
|
20
|
+
severity: warning
|
|
21
|
+
priority: high
|
|
22
|
+
languages: [python]
|
|
23
|
+
|
|
24
|
+
- id: no-empty-catch
|
|
25
|
+
type: pattern
|
|
26
|
+
pattern: "catch\\s*\\([^)]*\\)\\s*\\{\\s*\\}"
|
|
27
|
+
message: "Empty catch block silently swallows errors - log or re-throw"
|
|
28
|
+
severity: warning
|
|
29
|
+
priority: high
|
|
30
|
+
languages: [javascript, typescript]
|
|
31
|
+
|
|
32
|
+
# Medium: Pokemon exception handling
|
|
33
|
+
- id: no-catch-exception
|
|
34
|
+
type: pattern
|
|
35
|
+
pattern: "except\\s+Exception\\s*:"
|
|
36
|
+
message: "Catching Exception is too broad - catch specific exception types"
|
|
37
|
+
severity: info
|
|
38
|
+
priority: medium
|
|
39
|
+
languages: [python]
|
|
40
|
+
|
|
41
|
+
- id: no-catch-base-exception
|
|
42
|
+
type: pattern
|
|
43
|
+
pattern: "except\\s+BaseException\\s*:"
|
|
44
|
+
message: "Catching BaseException includes SystemExit/KeyboardInterrupt - use Exception or specific types"
|
|
45
|
+
severity: warning
|
|
46
|
+
priority: medium
|
|
47
|
+
languages: [python]
|
|
48
|
+
|
|
49
|
+
# Medium: Error suppression
|
|
50
|
+
- id: no-ignore-errors-flag
|
|
51
|
+
type: pattern
|
|
52
|
+
pattern: "errors\\s*=\\s*[\"']ignore[\"']"
|
|
53
|
+
message: "errors='ignore' silently drops data - use 'replace' or handle explicitly"
|
|
54
|
+
severity: warning
|
|
55
|
+
priority: medium
|
|
56
|
+
languages: [python]
|
|
57
|
+
|
|
58
|
+
# Info: Best practices
|
|
59
|
+
- id: prefer-contextlib-suppress
|
|
60
|
+
type: pattern
|
|
61
|
+
pattern: "except\\s+\\w+\\s*:\\s*\\n\\s*pass\\s*$"
|
|
62
|
+
message: "Consider contextlib.suppress() for intentionally ignoring specific exceptions"
|
|
63
|
+
severity: info
|
|
64
|
+
priority: low
|
|
65
|
+
languages: [python]
|
|
66
|
+
|
|
67
|
+
# LLM assertion for complex error handling patterns
|
|
68
|
+
- id: error-handling-semantic
|
|
69
|
+
type: llm
|
|
70
|
+
compliance: |
|
|
71
|
+
Check that error handling follows best practices:
|
|
72
|
+
1. Errors are not silently swallowed (no empty except/catch blocks that do nothing)
|
|
73
|
+
2. Exceptions are logged or reported before being suppressed
|
|
74
|
+
3. Error messages provide enough context to debug issues
|
|
75
|
+
4. Errors at API/system boundaries are handled (not just propagated)
|
|
76
|
+
message: "Error handling may need improvement"
|
|
77
|
+
severity: warning
|
|
78
|
+
priority: medium
|
|
79
|
+
languages: [python, javascript, typescript]
|
|
80
|
+
applicability:
|
|
81
|
+
exclude:
|
|
82
|
+
- "**/test_*.py"
|
|
83
|
+
- "**/*_test.py"
|
|
84
|
+
- "**/tests/**"
|