Files
admin b723e2bd7d Reorganize: Move all skills to skills/ folder
- Created skills/ directory
- Moved 272 skills to skills/ subfolder
- Kept agents/ at root level
- Kept installation scripts and docs at root level

Repository structure:
- skills/           - All 272 skills from skills.sh
- agents/           - Agent definitions
- *.sh, *.ps1       - Installation scripts
- README.md, etc.   - Documentation

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-23 18:05:17 +00:00

423 lines
14 KiB
Markdown

# Contributing to Claude Code Safety Net
First off, thanks for taking the time to contribute! This document provides guidelines and instructions for contributing to cc-safety-net.
## Table of Contents
- [Code of Conduct](#code-of-conduct)
- [Before You Start: Proposing New Features](#before-you-start-proposing-new-features)
- [Getting Started](#getting-started)
- [Prerequisites](#prerequisites)
- [Development Setup](#development-setup)
- [Testing Your Changes Locally](#testing-your-changes-locally)
- [Project Structure](#project-structure)
- [Development Workflow](#development-workflow)
- [Build Commands](#build-commands)
- [Code Style & Conventions](#code-style--conventions)
- [Making Changes](#making-changes)
- [Adding a Git Rule](#adding-a-git-rule)
- [Adding an rm Rule](#adding-an-rm-rule)
- [Adding Other Command Rules](#adding-other-command-rules)
- [Pull Request Process](#pull-request-process)
- [Publishing](#publishing)
- [Getting Help](#getting-help)
## Code of Conduct
Be respectful, inclusive, and constructive. We're all here to make better tools together.
## Before You Start: Proposing New Features
**Please open an issue to discuss new features before implementing them.**
This project has a focused scope: **preventing coding agents from making accidental mistakes that cause data loss** (e.g., `rm -rf ~/`, `git reset --hard`). It is NOT a general security hardening tool or an attack prevention system.
### Why Discuss First?
1. **Scope alignment** — Your idea might be great but outside the project's scope
2. **Approach feedback** — We can suggest the best way to implement it
3. **Avoid wasted effort** — Save time for both you and maintainers
### When to Open an Issue First
| Scenario | Open Issue First? |
|----------|-------------------|
| New detection rule (git, rm, etc.) | **Yes** |
| New command category to block | **Yes** |
| Architectural changes | **Yes** |
| New configuration options | **Yes** |
| Typo/documentation fixes | No, just PR |
| Small bug fixes with obvious solution | No, just PR |
### What to Include in Your Proposal
- **What** you want to add/change
- **Why** it fits the project scope (preventing accidental data loss)
- **Real-world scenario** where this would help
- Any **trade-offs** you've considered
A quick 5-minute issue can save hours of implementation time on both sides.
## Getting Started
### Prerequisites
- **Bun** - Required runtime and package manager ([install guide](https://bun.sh/docs/installation))
- **Claude Code** or **OpenCode** - For testing the plugin
### Development Setup
```bash
# Clone the repository
git clone https://github.com/kenryu42/claude-code-safety-net.git
cd claude-code-safety-net
# Install dependencies
bun install
# Build for distribution
bun run build
# Check for all lint errors, type errors, dead code and run tests
bun run check
```
### Testing Your Changes Locally
## Claude Code
1. **Build the project**:
```bash
bun run build
```
2. **Disable the safety-net plugin** in Claude Code (if installed) and exit Claude Code completely.
3. **Run Claude Code with the local plugin**:
```bash
claude --plugin-dir .
```
4. **Test blocked commands** to verify your changes:
```bash
# This should be blocked
git checkout -- README.md
# This should be allowed
git checkout -b test-branch
```
> [!NOTE]
> See the [official documentation](https://docs.anthropic.com/en/docs/claude-code/plugins#test-your-plugins-locally) for more details on testing plugins locally.
## OpenCode
1. **Build the project**:
```bash
bun run build
```
2. **Update your OpenCode config** (`~/.config/opencode/opencode.json` or `opencode.jsonc`):
```json
{
"plugin": [
"file:///absolute/path/to/cc-safety-net/dist/index.js"
]
}
```
For example, if your project is at `/Users/yourname/projects/cc-safety-net`:
```json
{
"plugin": [
"file:///Users/yourname/projects/cc-safety-net/dist/index.js"
]
}
```
> [!NOTE]
> Remove `"cc-safety-net"` from the plugin array if it exists, to avoid conflicts with the npm version.
> Or comment out the line if you're using `opencode.jsonc`.
3. **Restart OpenCode** to load the changes.
4. **Verify the plugin is loaded:** Run `/status` and confirm that the plugin name appears as `dist`.
5. **Test blocked commands** to verify your changes:
```bash
# This should be blocked
git checkout -- README.md
# This should be allowed
git checkout -b test-branch
```
> [!NOTE]
> See the [official documentation](https://opencode.ai/docs/plugins/) for more details on OpenCode plugins.
## Project Structure
```
claude-code-safety-net/
├── .claude-plugin/
│ ├── plugin.json # Plugin metadata
│ └── marketplace.json # Marketplace config
├── .github/
│ ├── workflows/ # CI/CD workflows
│ │ ├── ci.yml
│ │ ├── lint-github-actions-workflows.yml
│ │ └── publish.yml
│ └── pull_request_template.md
├── .husky/
│ └── pre-commit # Pre-commit hook (knip + lint-staged)
├── assets/
│ └── cc-safety-net.schema.json # JSON schema for config validation
├── ast-grep/
│ ├── rules/ # AST-grep rule definitions
│ ├── rule-tests/ # Rule test cases
│ └── utils/ # Shared utilities
├── commands/
│ ├── set-custom-rules.md # Slash command: configure custom rules
│ └── verify-custom-rules.md # Slash command: validate config
├── hooks/
│ └── hooks.json # Hook definitions
├── scripts/
│ ├── build-schema.ts # Generate JSON schema
│ ├── generate-changelog.ts # Changelog generation
│ └── publish.ts # Release automation
├── src/
│ ├── index.ts # OpenCode plugin export
│ ├── types.ts # Shared type definitions
│ ├── bin/
│ │ └── cc-safety-net.ts # Claude Code CLI entry point
│ └── core/
│ ├── analyze.ts # Main analysis orchestration
│ ├── analyze/ # Analysis submodules
│ │ ├── analyze-command.ts # Command analysis entry
│ │ ├── constants.ts # Shared constants
│ │ ├── dangerous-text.ts # Text pattern detection
│ │ ├── find.ts # find command analysis
│ │ ├── interpreters.ts # Interpreter one-liner detection
│ │ ├── parallel.ts # parallel command analysis
│ │ ├── rm-flags.ts # rm flag parsing
│ │ ├── segment.ts # Command segment analysis
│ │ ├── shell-wrappers.ts # Shell wrapper detection
│ │ ├── tmpdir.ts # Temp directory detection
│ │ └── xargs.ts # xargs command analysis
│ ├── audit.ts # Audit logging
│ ├── config.ts # Config loading
│ ├── custom-rules-doc.ts # Custom rules documentation
│ ├── env.ts # Environment variable utilities
│ ├── format.ts # Output formatting
│ ├── rules-git.ts # Git subcommand analysis
│ ├── rules-rm.ts # rm command analysis
│ ├── rules-custom.ts # Custom rule evaluation
│ ├── shell.ts # Shell parsing utilities
│ └── verify-config.ts # Config validator
├── tests/
│ ├── helpers.ts # Test utilities
│ ├── analyze-coverage.test.ts
│ ├── audit.test.ts
│ ├── config.test.ts
│ ├── custom-rules.test.ts
│ ├── custom-rules-integration.test.ts
│ ├── edge-cases.test.ts
│ ├── find.test.ts
│ ├── parsing-helpers.test.ts
│ ├── rules-git.test.ts
│ ├── rules-rm.test.ts
│ └── verify-config.test.ts
├── .lintstagedrc.json # Lint-staged config (biome + ast-grep)
├── biome.json # Linter/formatter config
├── knip.ts # Dead code detection config
├── package.json # Project config
├── sgconfig.yml # AST-grep config
├── tsconfig.json # TypeScript config
├── tsconfig.typecheck.json # Type-check only config
├── AGENTS.md # AI agent guidelines
├── CLAUDE.md # Claude Code context
└── README.md # Project documentation
```
| Module | Purpose |
|--------|---------|
| `analyze.ts` | Main entry, command analysis orchestration |
| `analyze/` | Submodules for specific analysis tasks (find, xargs, parallel, interpreters, etc.) |
| `audit.ts` | Audit logging to `~/.cc-safety-net/logs/` |
| `config.ts` | Config loading (`.safety-net.json`, `~/.cc-safety-net/config.json`) |
| `env.ts` | Environment variable utilities (`envTruthy`) |
| `format.ts` | Output formatting (`formatBlockedMessage`) |
| `rules-git.ts` | Git rules (checkout, restore, reset, clean, push, branch, stash) |
| `rules-rm.ts` | rm analysis (cwd-relative, temp paths, root/home detection) |
| `rules-custom.ts` | Custom rule matching |
| `shell.ts` | Shell parsing (`splitShellCommands`, `shlexSplit`, `stripWrappers`) |
| `verify-config.ts` | Config file validation |
## Development Workflow
### Build Commands
```bash
# Run all checks (lint, type check, dead code, ast-grep scan, tests)
bun run check
# Individual commands
bun run lint # Lint + format (Biome)
bun run typecheck # Type check
bun run knip # Dead code detection
bun run sg:scan # AST pattern scan
bun test # Run tests
# Run specific test
bun test tests/rules-git.test.ts
# Run tests matching pattern
bun test --test-name-pattern "checkout"
# Build for distribution
bun run build
```
### Code Style & Conventions
| Convention | Rule |
|------------|------|
| Runtime | **Bun** |
| Package Manager | **bun only** (`bun install`, `bun run`) |
| Formatter/Linter | **Biome** |
| Type Hints | Required on all functions |
| Type Syntax | `type \| null` preferred over `type \| undefined` |
| File Naming | `kebab-case` (e.g., `rules-git.ts`, not `rulesGit.ts`) |
| Function Naming | `camelCase` for functions, `PascalCase` for types/interfaces |
| Constants | `SCREAMING_SNAKE_CASE` for reason constants |
| Imports | Relative imports within package |
**Examples**:
```typescript
// Good
export function analyzeCommand(
command: string,
options?: { strict?: boolean }
): string | null {
// ...
}
// Bad
export function analyzeCommand(command, options) { // Missing type hints
// ...
}
```
**Anti-Patterns (Do Not Do)**:
- Using npm/yarn/pnpm instead of bun
- Suppressing type errors with `@ts-ignore` or `any`
- Skipping tests for new rules
- Modifying version in `package.json` directly
## Making Changes
### Adding a Git Rule
1. **Add reason constant** in `src/core/rules-git.ts`:
```typescript
const REASON_MY_RULE = "git my-command does something dangerous. Use safer alternative.";
```
2. **Add detection logic** in `analyzeGit()`:
```typescript
if (subcommand === "my-command" && tokens.includes("--dangerous-flag")) {
return REASON_MY_RULE;
}
```
3. **Add tests** in `tests/rules-git.test.ts`:
```typescript
describe("git my-command", () => {
test("dangerous flag blocked", () => {
assertBlocked("git my-command --dangerous-flag", "dangerous");
});
test("safe flag allowed", () => {
assertAllowed("git my-command --safe-flag");
});
});
```
4. **Run checks**:
```bash
bun run check
```
### Adding an rm Rule
1. **Add logic** in `src/core/rules-rm.ts`
2. **Add tests** in `tests/rules-rm.test.ts`
3. **Run checks**: `bun run check`
### Adding Other Command Rules
1. **Add reason constant** in `src/core/analyze.ts`:
```typescript
const REASON_MY_COMMAND = "my-command is dangerous because...";
```
2. **Add detection** in `analyzeSegment()`
3. **Add tests** in the appropriate test file
4. **Run checks**: `bun run check`
### Edge Cases to Test
When adding rules, ensure you test these edge cases:
- Shell wrappers: `bash -c '...'`, `sh -lc '...'`
- Sudo/env prefixes: `sudo git ...`, `env VAR=1 git ...`
- Pipelines: `echo ok | git reset --hard`
- Interpreter one-liners: `python -c 'os.system("...")'`
- Xargs/parallel: `find . | xargs rm -rf`
- Busybox: `busybox rm -rf /`
## Pull Request Process
1. **Fork** the repository and create your branch from `main`
2. **Make changes** following the conventions above
3. **Run all checks** locally:
```bash
bun run check # Must pass with no errors
```
4. **Test in Claude Code and OpenCode** using the local plugin method described above
5. **Commit** with clear, descriptive messages:
- Use present tense ("Add rule" not "Added rule")
- Reference issues if applicable ("Fix #123")
6. **Push** to your fork and create a Pull Request
7. **Describe** your changes clearly in the PR description
### PR Checklist
- [ ] Code follows project conventions (type hints, naming, etc.)
- [ ] `bun run check` passes (lint, types, dead code, tests)
- [ ] Tests added for new rules
- [ ] Tested locally with Claude Code and Opencode
- [ ] Updated documentation if needed (README, AGENTS.md)
- [ ] No version changes in `package.json`
## Publishing
**Important**: Version bumping and releases are handled by maintainers only.
- **Never** modify the version in `package.json` or `plugin.json` directly
- Maintainers handle versioning, tagging, and releases
## Getting Help
- **Project Knowledge**: Check `CLAUDE.md` or `AGENTS.md` for detailed architecture and conventions
- **Code Patterns**: Review existing implementations in `src/core/`
- **Test Patterns**: See `tests/helpers.ts` for test utilities
- **Issues**: Open an issue for bugs or feature requests
---
Thank you for contributing to Claude Code Safety Net! Your efforts help keep AI-assisted coding safer for everyone.