Skip to content
Open
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
3ad046c
chore(cubebot): scaffold cloudflare worker app
alvarosabu Jan 27, 2026
8f68df3
chore(cubebot): add d1 schema and types
alvarosabu Jan 27, 2026
9a63bb9
feat(cubebot): add github webhook signature verification
alvarosabu Jan 27, 2026
d3dc685
feat(cubebot): add github api client and helpers
alvarosabu Jan 27, 2026
da52d0f
feat(cubebot): add issue detection logic
alvarosabu Jan 27, 2026
a5c8feb
feat(cubebot): add rag retrieval with workers ai embeddings
alvarosabu Jan 27, 2026
201e28d
feat(cubebot): add claude integration for issue analysis
alvarosabu Jan 27, 2026
7e3b26d
feat(cubebot): add comment formatters
alvarosabu Jan 27, 2026
72ea6eb
feat(cubebot): add issue opened handler
alvarosabu Jan 27, 2026
6419f44
feat(cubebot): add comment handler for @mentions
alvarosabu Jan 27, 2026
bcbc669
feat(cubebot): add docs embedding script
alvarosabu Jan 27, 2026
07cf067
docs(cubebot): add readme and project documentation
alvarosabu Jan 27, 2026
3137bda
feat(cubebot): add admin endpoint to seed doc embeddings
alvarosabu Jan 29, 2026
01f0aa8
docs(cubebot): update readme with correct deployment steps
alvarosabu Jan 29, 2026
4c3a81d
feat(cubebot): integrate @octokit/auth-app for GitHub authentication …
alvarosabu Jan 29, 2026
76bf160
feat(cubebot): implement feasibility analysis for issues with code co…
alvarosabu Jan 29, 2026
55e6ac9
fix(cubebot): update prompt package format and improve issue triage l…
alvarosabu Jan 29, 2026
180b546
feat(cubebot): rename and refactor issue conversion to discussion logic
alvarosabu Jan 29, 2026
216e00f
Merge branch 'main' into chore/tres-212-github-cubebot-automation
alvarosabu Feb 27, 2026
2b77452
feat(github): modify issue handling to skip feature request redirecti…
alvarosabu Feb 27, 2026
6e49757
feat(github): add GitHub Actions workflow for deploying CubeBot to Cl…
alvarosabu Feb 27, 2026
00b311a
chore(cubebot): remove outdated CubeBot implementation plan document
alvarosabu Feb 27, 2026
78dc25a
chore(cubebot): update dependencies in package.json and pnpm-lock.yam…
alvarosabu Feb 27, 2026
a3a8e76
chore(github): add pull request trigger for CubeBot deployment workflow
alvarosabu Feb 27, 2026
ada19c2
chore(github): update deploy command in CubeBot workflow to use 'run'…
alvarosabu Feb 27, 2026
33c2933
feat(cubebot): enhance author comment formatting with greeting and jo…
alvarosabu Feb 27, 2026
20af764
Update apps/cubebot/README.md
alvarosabu Feb 27, 2026
6886d28
chore(cubebot): improve seed documentation process and enhance securi…
alvarosabu Feb 27, 2026
c902218
Merge branch 'main' into chore/tres-212-github-cubebot-automation
alvarosabu Mar 9, 2026
64309f9
Merge branch 'main' into chore/tres-212-github-cubebot-automation
alvarosabu Mar 10, 2026
e15867c
chore: update hono dependency to version 4.12.12 and clean up TypeScr…
alvarosabu Apr 11, 2026
cab270d
Merge branch 'main' into chore/tres-212-github-cubebot-automation
alvarosabu Apr 11, 2026
66f8d07
chore: add undici override to package.json and pnpm-lock.yaml
alvarosabu Apr 11, 2026
829ac39
Merge branch 'main' into chore/tres-212-github-cubebot-automation
alvarosabu Apr 11, 2026
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
29 changes: 29 additions & 0 deletions .github/workflows/deploy-cubebot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Deploy CubeBot

on:
push:
branches:
- main
paths:
- 'apps/cubebot/**'
pull_request:
branches:
- main
paths:
- 'apps/cubebot/**'

jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy to Cloudflare Workers
steps:
- name: Checkout
uses: actions/checkout@v4

- uses: ./.github/actions/setup-node

- name: Deploy
run: pnpm --filter cubebot run deploy
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
3 changes: 3 additions & 0 deletions apps/cubebot/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
data/
.wrangler/
.dev.vars
33 changes: 33 additions & 0 deletions apps/cubebot/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# CubeBot

GitHub App bot for TresJS issue triage built on Cloudflare Workers.

## Tech Stack

- **Runtime:** Cloudflare Workers
- **Framework:** Hono
- **Database:** Cloudflare D1
- **AI:** Anthropic Claude + Workers AI (embeddings)
- **GitHub:** Octokit

## Key Files

- `src/index.ts` - Worker entry point, webhook routing
- `src/github/handlers/` - Event handlers for issues and comments
- `src/ai/` - Claude integration and RAG retrieval
- `src/triage/` - Issue detection and comment formatting
- `scripts/embed-docs.ts` - Documentation embedding script

## Testing Locally

```bash
pnpm dev
# Expose via ngrok for webhook testing
```

## Secrets Required

- `GITHUB_APP_ID`
- `GITHUB_PRIVATE_KEY`
- `GITHUB_WEBHOOK_SECRET`
- `ANTHROPIC_API_KEY`
98 changes: 98 additions & 0 deletions apps/cubebot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# CubeBot 🧊

GitHub App bot for TresJS issue triage and documentation assistance.

## Features

- Auto-triages new issues (bug vs feature detection)
- Redirects feature requests to GitHub Discussions
- Checks for required bug report info (reproduction, system info)
- Suggests relevant documentation via RAG
- Responds to @mentions with doc-based assistance

## Setup

### 1. Create GitHub App

1. Go to github.com/settings/apps → New GitHub App
2. Name: `tresjs-cubebot`
3. Permissions:
- Issues: Read & Write
- Discussions: Read & Write
4. Subscribe to events: `issues`, `issue_comment`
5. Generate and download private key (`.pem` file)

### 2. Cloudflare Setup

```bash
# Create D1 database
pnpm wrangler d1 create cubebot-docs

# Copy the database_id from output and update wrangler.toml

# Apply schema to remote D1
cd apps/cubebot
pnpm wrangler d1 execute cubebot-docs --remote --file=schema.sql
```

### 3. Deploy Worker

```bash
cd apps/cubebot
pnpm wrangler deploy
```

### 4. Set Secrets

```bash
cd apps/cubebot

# App ID from GitHub App page
pnpm wrangler secret put GITHUB_APP_ID

# Contents of the .pem private key file
pnpm wrangler secret put GITHUB_PRIVATE_KEY

# Generate with: openssl rand -hex 32
# Use same value in GitHub App webhook settings
pnpm wrangler secret put GITHUB_WEBHOOK_SECRET

# From console.anthropic.com
pnpm wrangler secret put ANTHROPIC_API_KEY
```

### 5. Configure GitHub App Webhook

1. Go to your GitHub App settings
2. Set Webhook URL: `https://tresjs-cubebot.<account>.workers.dev/webhook`
3. Set Webhook secret: same value as `GITHUB_WEBHOOK_SECRET`
4. Save

### 6. Seed Documentation (RAG)

```bash
# Call the admin endpoint to fetch docs and generate embeddings
curl -X POST https://tresjs-cubebot.<account>.workers.dev/admin/seed-docs
Comment thread
alvarosabu marked this conversation as resolved.
Outdated
```

This fetches `llms-full.txt` from TresJS doc sites, generates embeddings via Workers AI, and stores them in D1.

### 7. Install GitHub App

Install the app on the TresJS organization or specific repositories.

## Development

```bash
cd apps/cubebot

# Start local dev server
pnpm wrangler dev

# Use ngrok to expose for webhook testing
ngrok http 8787
```

## Architecture

See [design doc](../../.claude/plans/2026-01-27-cubebot-design.md) for full architecture details.
5 changes: 5 additions & 0 deletions apps/cubebot/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { tresLintConfig } from '@tresjs/eslint-config'

export default tresLintConfig({
ignores: ['**/*.md'],
})
26 changes: 26 additions & 0 deletions apps/cubebot/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "cubebot",
"type": "module",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "wrangler dev",
"deploy": "wrangler deploy",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"typecheck": "tsc --noEmit",
"embed-docs": "npx tsx scripts/embed-docs.ts"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.78.0",
"@octokit/auth-app": "^8.2.0",
"hono": "^4.12.3",
"octokit": "^5.0.5"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20260226.1",
"@tresjs/eslint-config": "workspace:^",
"typescript": "catalog:typescript",
"wrangler": "^4.69.0"
}
}
11 changes: 11 additions & 0 deletions apps/cubebot/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CREATE TABLE IF NOT EXISTS doc_chunks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
source TEXT NOT NULL,
url TEXT NOT NULL,
title TEXT NOT NULL,
content TEXT NOT NULL,
embedding TEXT NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_doc_chunks_source ON doc_chunks(source);
92 changes: 92 additions & 0 deletions apps/cubebot/scripts/embed-docs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { execSync } from 'node:child_process'

const DOC_SOURCES = [
{ name: 'core', url: 'https://docs.tresjs.org/llms-full.txt' },
{ name: 'cientos', url: 'https://cientos.tresjs.org/llms-full.txt' },
{ name: 'postprocessing', url: 'https://post-processing.tresjs.org/llms-full.txt' },
]

interface DocChunk {
source: string
url: string
title: string
content: string
}

async function fetchDocs(source: { name: string; url: string }): Promise<DocChunk[]> {
console.log(`Fetching ${source.name} docs from ${source.url}...`)

try {
const response = await fetch(source.url)
if (!response.ok) {
console.warn(`Failed to fetch ${source.url}: ${response.status}`)
return []
}

const text = await response.text()
return parseDocsIntoChunks(source.name, source.url, text)
}
catch (error) {
console.warn(`Error fetching ${source.url}:`, error)
return []
}
}

function parseDocsIntoChunks(source: string, baseUrl: string, text: string): DocChunk[] {
const chunks: DocChunk[] = []

// Split by headers (## or #)
const sections = text.split(/(?=^#{1,2}\s)/m)

for (const section of sections) {
if (section.trim().length < 50) continue // Skip tiny sections

const titleMatch = section.match(/^#{1,2}\s+(.+)$/m)
const title = titleMatch?.[1] ?? 'Untitled'

// Extract URL if present in the section
const urlMatch = section.match(/https?:\/\/[^\s)]+/)
const url = urlMatch?.[0] ?? baseUrl

chunks.push({
source,
url,
title: title.trim(),
content: section.slice(0, 2000), // Limit chunk size
})
}

return chunks
}

async function main() {
console.log('Starting docs embedding process...\n')

const allChunks: DocChunk[] = []

for (const source of DOC_SOURCES) {
const chunks = await fetchDocs(source)
allChunks.push(...chunks)
console.log(` → ${chunks.length} chunks from ${source.name}`)
}

console.log(`\nTotal chunks: ${allChunks.length}`)

// Write chunks to a JSON file for manual D1 import
// In production, this would call Workers AI for embeddings
const outputPath = 'apps/cubebot/data/doc-chunks.json'

// Create data directory
execSync('mkdir -p apps/cubebot/data')

const fs = await import('node:fs')
fs.writeFileSync(outputPath, JSON.stringify(allChunks, null, 2))

console.log(`\nChunks written to ${outputPath}`)
console.log('\nNext steps:')
console.log('1. Run wrangler d1 execute to create the database')
console.log('2. Use a separate script to generate embeddings via Workers AI')
console.log('3. Import the embeddings into D1')
}

main().catch(console.error)
Loading
Loading