Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 41 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
## Development Setup

```bash
# Clone the repo
git clone https://github.com/codercops/chatcops.git
# Fork the repo on GitHub, then clone your fork
git clone https://github.com/<your-username>/chatcops.git
cd chatcops

# Add upstream remote
git remote add upstream https://github.com/codercops/chatcops.git

# Install dependencies
pnpm install

Expand All @@ -32,12 +35,44 @@ website/ - Marketing site + Starlight docs (Astro)

## Development Workflow

1. Create a branch: `git checkout -b feature/my-feature`
2. Make changes
1. Sync your fork's `dev` branch with upstream:
```bash
git fetch upstream
git checkout dev
git merge upstream/dev
```
2. Work directly on your fork's `dev` branch — commit and push as you go
3. Run tests: `pnpm test`
4. Run typecheck: `pnpm -r typecheck`
5. Add a changeset: `pnpm changeset`
6. Open a PR
5. Add a changeset if your changes affect published packages: `pnpm changeset`
6. When ready, open a PR from your fork's `dev` → upstream `dev`

### Branch Strategy

- **`dev`** — default branch, all development happens here
- **`production`** — release branch, merged from `dev` when cutting a release
- PRs should target **`dev`** unless you're doing a production release

### Releasing to Production

When `dev` is ready to ship:

1. Ensure all changes that affect published packages have changesets:
```bash
pnpm changeset
```
This creates a changeset file describing the version bump (patch/minor/major) and a summary. Commit it to `dev`.

2. Create a PR from `dev` → `production` and merge it.

3. The **Release** workflow runs automatically on `production` push:
- Builds, typechecks, and tests everything
- If unreleased changesets exist, Changesets action opens a **"Version Packages"** PR on `production` that bumps versions and updates changelogs
- Merging that PR triggers the workflow again, which **publishes to npm** (via OIDC trusted publishing)

4. The website is auto-deployed by the Vercel GitHub integration — no manual step needed.

> **Note:** Only maintainers can merge into `production`. If you're a contributor, just make sure your PR to `dev` includes a changeset when needed.

## Widget Development

Expand Down
129 changes: 129 additions & 0 deletions packages/core/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# @chatcops/core

Core AI provider abstraction, tools, knowledge base, conversation management, analytics, and i18n for ChatCops.

## Install

```bash
npm install @chatcops/core
```

## Providers

Unified interface for Claude, OpenAI, and Gemini. Supports streaming (AsyncGenerator) and sync modes.

```typescript
import { createProvider } from '@chatcops/core';

const provider = await createProvider({
type: 'claude', // 'claude' | 'openai' | 'gemini'
apiKey: process.env.ANTHROPIC_API_KEY,
model: 'claude-haiku-4-5-20251001', // optional
});

// Streaming
for await (const chunk of provider.chat({
messages: [{ id: '1', role: 'user', content: 'Hello', timestamp: Date.now() }],
systemPrompt: 'You are a helpful assistant.',
})) {
process.stdout.write(chunk);
}

// Sync
const response = await provider.chatSync({
messages: [{ id: '1', role: 'user', content: 'Hello', timestamp: Date.now() }],
systemPrompt: 'You are a helpful assistant.',
});
```

Default models: `claude-haiku-4-5-20251001` (Claude), `gpt-4o-mini` (OpenAI), `gemini-2.0-flash` (Gemini).

## Tools

Define tools the AI can call. Built-in `LeadCaptureTool` for collecting contact info.

```typescript
import { LeadCaptureTool } from '@chatcops/core';

const leadTool = new LeadCaptureTool((lead) => {
console.log('Lead captured:', lead.name, lead.email);
});
```

Custom tools implement the `ChatTool` interface:

```typescript
import type { ChatTool, ToolResult } from '@chatcops/core';

const myTool: ChatTool = {
name: 'lookup_order',
description: 'Look up an order by ID',
parameters: {
orderId: { type: 'string', description: 'The order ID' },
},
required: ['orderId'],
async execute(input): Promise<ToolResult> {
return { success: true, data: { status: 'shipped' } };
},
};
```

## Knowledge Base

Feed context to the AI with text chunks or FAQ pairs.

```typescript
import { TextKnowledgeSource, FAQKnowledgeSource } from '@chatcops/core';

const text = new TextKnowledgeSource([
'ChatCops supports Claude, OpenAI, and Gemini.',
'The widget is zero-dependency and uses Shadow DOM.',
]);

const faq = new FAQKnowledgeSource([
{ question: 'What is ChatCops?', answer: 'An embeddable AI chat widget.' },
{ question: 'Is it free?', answer: 'Yes, MIT licensed.' },
]);

const context = await faq.getContext('is chatcops free');
```

## Conversation Management

```typescript
import { ConversationManager } from '@chatcops/core';

const manager = new ConversationManager({ maxMessages: 100 });
const conversation = await manager.getOrCreate('conv-123');
await manager.addMessage('conv-123', {
id: '1', role: 'user', content: 'Hello', timestamp: Date.now(),
});
```

Plug in your own store by implementing `ConversationStore`.

## Analytics

```typescript
import { AnalyticsCollector } from '@chatcops/core';

const analytics = new AnalyticsCollector();
analytics.track('message:sent', { conversationId: 'conv-123' });
const stats = analytics.getStats();
```

## i18n

8 built-in locales: `en`, `es`, `hi`, `fr`, `de`, `ja`, `zh`, `ar`.

```typescript
import { getLocale, getAvailableLocales } from '@chatcops/core';

const strings = getLocale('es');
console.log(strings.welcomeMessage); // "Hola! Como puedo ayudarte hoy?"
console.log(getAvailableLocales()); // ['en', 'es', 'hi', 'fr', 'de', 'ja', 'zh', 'ar']
```

## License

MIT
164 changes: 164 additions & 0 deletions packages/server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# @chatcops/server

Server-side handler and platform adapters for ChatCops. Handles streaming responses, rate limiting, webhooks, and analytics.

## Install

```bash
npm install @chatcops/server @chatcops/core
```

## Quick Start

### Express

```typescript
import express from 'express';
import { chatcopsMiddleware } from '@chatcops/server';

const app = express();
app.use(express.json());

app.post('/chat', chatcopsMiddleware({
provider: {
type: 'claude',
apiKey: process.env.ANTHROPIC_API_KEY,
},
systemPrompt: 'You are a helpful assistant for our website.',
cors: '*',
}));

app.listen(3001);
```

### Vercel Edge

```typescript
import { chatcopsVercelHandler } from '@chatcops/server';

const handler = chatcopsVercelHandler({
provider: { type: 'openai', apiKey: process.env.OPENAI_API_KEY },
systemPrompt: 'You are a helpful assistant.',
cors: '*',
});

export const POST = (req: Request) => handler(req);
export const OPTIONS = (req: Request) => handler(req);
export const config = { runtime: 'edge' };
```

### Cloudflare Workers

```typescript
import { chatcopsCloudflareHandler } from '@chatcops/server';

export default {
async fetch(request: Request, env: Env) {
const handler = chatcopsCloudflareHandler({
provider: { type: 'claude', apiKey: env.ANTHROPIC_API_KEY },
systemPrompt: 'You are a helpful assistant.',
cors: '*',
});
return handler(request);
},
};
```

## Configuration

```typescript
import { createChatHandler } from '@chatcops/server';
import { LeadCaptureTool, FAQKnowledgeSource } from '@chatcops/core';

const handler = createChatHandler({
// AI provider (required)
provider: {
type: 'claude', // 'claude' | 'openai' | 'gemini'
apiKey: 'sk-...',
model: 'claude-haiku-4-5-20251001',
},

// System prompt (required)
systemPrompt: 'You are a helpful support assistant.',

// Tools (optional)
tools: [
new LeadCaptureTool((lead) => {
console.log('New lead:', lead.email);
}),
],

// Knowledge base (optional)
knowledge: [
new FAQKnowledgeSource([
{ question: 'What do you do?', answer: 'We build software.' },
]),
],

// Rate limiting (optional, default: 30 req/60s)
rateLimit: { maxRequests: 10, windowMs: 60_000 },

// Webhooks (optional)
webhooks: [
{ url: 'https://hooks.example.com/leads', events: ['lead:captured'] },
],

// Analytics (optional)
analytics: true,

// CORS origin (required)
cors: '*',
});
```

## SSE Stream Format

All adapters produce identical Server-Sent Events:

```
data: {"content":"Hello"}
data: {"content":" there!"}
data: {"done":true}
data: [DONE]
```

On error:

```
data: {"error":"rate_limit","retryAfter":42}
data: [DONE]
```

Error codes: `invalid_request`, `rate_limit`, `provider_error`, `internal_error`.

## Rate Limiting

In-memory, per-IP. Suitable for single-instance deployments.

```typescript
import { RateLimiter } from '@chatcops/server';

const limiter = new RateLimiter({ maxRequests: 30, windowMs: 60_000 });
const result = limiter.check('127.0.0.1');
// { allowed: true } or { allowed: false, retryAfter: 42 }
```

## Webhooks

HMAC-SHA256 signed, with exponential backoff retries (up to 3 attempts).

```typescript
import { WebhookDispatcher } from '@chatcops/server';

const dispatcher = new WebhookDispatcher([
{ url: 'https://hooks.example.com/chat', events: ['*'], secret: 'my-secret' },
]);

await dispatcher.dispatch('lead:captured', { email: 'user@example.com' });
```

Signature sent via `X-ChatCops-Signature` header.

## License

MIT
Loading
Loading