# Contributing to OPC Agent

Thanks for your interest in contributing! This guide covers everything you need to get started.

---

## Development Setup

```bash
# 1. Fork and clone
git clone https://github.com/Deepleaper/opc-agent.git
cd opc-agent

# 2. Install dependencies
npm install

# 3. Build (TypeScript → CommonJS)
npx tsc

# 4. Run tests
npx vitest run
```

> **Note:** This is a **CommonJS** project (not ESM). All output goes to `dist/`. TypeScript 5.x is required. There are **no native dependencies** — the project uses `sql.js` (WASM) instead of `better-sqlite3` so it works on all platforms without compilation.

---

## Project Structure

```
src/
  core/          # Agent loop, runtime, session management, OutputGuard
  channels/      # One file per channel: telegram.ts, web.ts, slack.ts, …
  memory/        # DeepBrainMemoryStore, InMemoryStore, vector search
  tools/         # Built-in tools (file ops, web fetch, shell exec, etc.)
  schema/        # Zod schemas and shared TypeScript types
  skills/        # Skill loader, BaseSkill, SkillPlatformFilter
  evolution/     # L1–L4 self-evolution engine
  workflow/      # WorkflowEngine, SandboxManager, SubAgentManager
  cli/           # CLI entry point and command handlers

examples/        # Runnable usage examples
test/            # Test files, co-located with source or in test/
```

---

## Testing

The project has **1114+ tests** covering unit, integration, and end-to-end scenarios.

```bash
# Run all tests
npx vitest run

# Run in watch mode during development
npx vitest

# Run a specific test file
npx vitest run src/core/output-guard.test.ts
```

**Conventions:**
- Test files live next to the code they test (`foo.ts` → `foo.test.ts`) or under `test/`.
- Use `describe` / `it` / `expect` from Vitest — no Jest imports.
- Mock at the boundary: stub external HTTP calls and file I/O, but don't mock internal modules unless unavoidable.
- Every new feature or bug fix must include a corresponding test.

---

## Adding a Channel

Channels live in `src/channels/`. Each channel extends `BaseChannel` and plugs into the `AgentRuntime`.

1. **Create the file**: `src/channels/my-channel.ts`

```typescript
import { BaseChannel, type OutboundMessage } from '../core/base-channel';

export class MyChannel extends BaseChannel {
  readonly name = 'my-channel';

  async start(): Promise<void> {
    // Connect to the external service
  }

  async stop(): Promise<void> {
    // Disconnect cleanly
  }

  protected async send(message: OutboundMessage): Promise<void> {
    // Deliver the message — OutputGuard has already run at this point
  }
}
```

2. **Register it** in `src/core/runtime.ts` alongside existing channels.

3. **Add a test** in `src/channels/my-channel.test.ts`.

4. **Document it** in the CLI table in `README.md` and `README.zh-CN.md`.

> OutputGuard runs automatically on every message before `send()` is called — you do not need to add sanitization logic in your channel.

---

## Adding a Built-in Tool

Built-in tools live in `src/tools/`. Each tool implements the `BuiltinTool` interface and is auto-discovered at runtime.

1. **Create the file**: `src/tools/my-tool.ts`

```typescript
import { type BuiltinTool, type ToolResult } from '../schema/tool';

export const myTool: BuiltinTool = {
  name: 'my_tool',
  description: 'What this tool does, for the model to read',
  parameters: {
    type: 'object',
    properties: {
      input: { type: 'string', description: 'The input value' },
    },
    required: ['input'],
  },
  async execute(args: { input: string }): Promise<ToolResult> {
    return { content: `Result: ${args.input}` };
  },
};
```

2. **Export it** from `src/tools/index.ts`.

3. **Add a test** in `src/tools/my-tool.test.ts`.

---

## Commit Convention

| Prefix | Use for |
|--------|---------|
| `feat:` | New feature |
| `fix:` | Bug fix |
| `docs:` | Documentation only |
| `test:` | Tests only |
| `refactor:` | Code restructuring without behavior change |
| `chore:` | Build, tooling, dependencies |

Examples:
```
feat: add WeChat Work channel
fix: OutputGuard false-positive on base64 strings
docs: add channel mini-guide to CONTRIBUTING
```

---

## Pull Request Checklist

- [ ] `npx tsc` passes with no errors
- [ ] `npx vitest run` — all tests pass, new tests added
- [ ] No `.js` output files committed (build artifacts are gitignored)
- [ ] Docs updated if you added or changed a public API or CLI command

---

## Questions?

Open a [Discussion](https://github.com/Deepleaper/opc-agent/discussions) for design questions or [Issues](https://github.com/Deepleaper/opc-agent/issues) for bugs.

---

## License

By contributing, you agree that your contributions will be licensed under **Apache-2.0**.
