diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f56c68ae..dddbfd7e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -120,7 +120,7 @@ This is enforced by commitlint via lefthook pre-commit hooks. See [CLAUDE.md](./CLAUDE.md) for a detailed architecture overview. Key directories: - `src/router/` — Webhook receiver (enqueues jobs to Redis) -- `src/triggers/` — Event handlers (Trello, GitHub, JIRA) +- `src/triggers/` — Event handlers (Trello, JIRA, Linear, GitHub) - `src/agents/` — AI agent implementations - `src/gadgets/` — Tools agents can use - `src/api/` — Dashboard API (tRPC) diff --git a/README.md b/README.md index 1dc3aadb..ab1eb33a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Node.js 22+](https://img.shields.io/badge/node-%3E%3D22-brightgreen)](https://nodejs.org/) -> **Cascade orchestrates AI agents (Claude Code, Codex, opencode, LLMist) across your workflows in GitHub, Trello, and Jira.** +> **Cascade orchestrates AI agents (Claude Code, Codex, opencode, LLMist) across your workflows in GitHub, Trello, Jira, and Linear.** Cascade is an open-source platform that automates the full software development lifecycle. Connect your PM tool and GitHub repository, and Cascade drives work items from plan to merge: @@ -38,7 +38,7 @@ For the full setup walkthrough — projects, credentials, webhooks, and triggers ## ⚡ Features -- **Multi-PM support** — Works with Trello and JIRA out of the box +- **Multi-PM support** — Works with Trello, JIRA, and Linear out of the box - **11 agent types** — Splitting, planning, implementation, review, debug, respond-to-review, respond-to-CI, and more - **Dual-persona GitHub model** — Separate implementer and reviewer bot accounts to prevent feedback loops - **Web dashboard + CLI** — Monitor runs, manage projects, configure triggers @@ -151,7 +151,7 @@ All project-level credentials (GitHub tokens, PM keys, LLM API keys) are stored **Dual-persona GitHub model** — Cascade uses two separate GitHub bot accounts per project (implementer and reviewer) to prevent feedback loops. The implementer writes code and creates PRs; the reviewer reviews and approves them. -**Trigger system** — Events from Trello, JIRA, and GitHub webhooks are matched against registered `TriggerHandler` instances. Triggers are configured per-project in the database. +**Trigger system** — Events from Trello, JIRA, Linear, and GitHub webhooks are matched against registered `TriggerHandler` instances. Triggers are configured per-project in the database. **Agent engines** — Agents run through a shared execution lifecycle with a pluggable engine registry. Default engine is `claude-code` (Anthropic Claude Code SDK). Alternatives: `llmist` (supports OpenRouter, Anthropic, OpenAI), `codex` (OpenAI Codex CLI), `opencode` (OpenCode server). diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 6562a2ef..ddf52cd8 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,6 +1,6 @@ # CASCADE Architecture -CASCADE is a PM-to-Code automation platform that connects project management tools (Trello, JIRA), source control (GitHub), and monitoring (Sentry) to AI-powered agents that autonomously implement features, review PRs, debug failures, and manage backlogs. Webhooks from external providers flow through a router, get queued in Redis, and are processed by ephemeral worker containers that run agents against cloned repositories. +CASCADE is a PM-to-Code automation platform that connects project management tools (Trello, JIRA, Linear), source control (GitHub), and monitoring (Sentry) to AI-powered agents that autonomously implement features, review PRs, debug failures, and manage backlogs. Webhooks from external providers flow through a router, get queued in Redis, and are processed by ephemeral worker containers that run agents against cloned repositories. > **Relationship to CLAUDE.md**: `CLAUDE.md` is the operational reference (commands, env vars, how-to). This document and its deep-dives cover the *system design* — how components fit together and why. @@ -11,6 +11,7 @@ graph TB subgraph External["External Providers"] Trello JIRA + Linear GitHub Sentry end @@ -30,6 +31,7 @@ graph TB Trello -->|webhook| Router JIRA -->|webhook| Router + Linear -->|webhook| Router GitHub -->|webhook| Router Sentry -->|webhook| Router @@ -39,6 +41,7 @@ graph TB Worker -->|PRs, comments| GitHub Worker -->|status updates| Trello Worker -->|status updates| JIRA + Worker -->|status updates| Linear Router <--> DB Worker <--> DB @@ -65,7 +68,7 @@ The canonical path from webhook to pull request: ```mermaid sequenceDiagram - participant P as Provider
(Trello/GitHub/JIRA/Sentry) + participant P as Provider
(Trello/JIRA/Linear/GitHub/Sentry) participant R as Router participant Q as Redis/BullMQ participant W as Worker @@ -110,10 +113,11 @@ sequenceDiagram | `src/backends/` | LLM execution engines: Claude Code, LLMist, Codex, OpenCode | | `src/gadgets/` | Tool implementations agents use (file ops, PM, SCM, alerting, shell) | | `src/integrations/` | Unified integration interfaces, registry, bootstrap | -| `src/pm/` | PM abstraction layer: provider interface, Trello/JIRA adapters, lifecycle | +| `src/pm/` | PM abstraction layer: provider interface, Trello/JIRA/Linear adapters, lifecycle | | `src/github/` | GitHub API client, dual-persona model, PR operations | | `src/trello/` | Trello API client | | `src/jira/` | JIRA API client (jira.js wrapper) | +| `src/linear/` | Linear GraphQL API client | | `src/sentry/` | Sentry API client, alerting integration | | `src/config/` | Configuration provider, caching, credential resolution, integration roles | | `src/db/` | Drizzle ORM schema, repositories, migrations | diff --git a/docs/architecture/01-services.md b/docs/architecture/01-services.md index f2769834..0f46957b 100644 --- a/docs/architecture/01-services.md +++ b/docs/architecture/01-services.md @@ -48,6 +48,7 @@ The router is the webhook ingestion point. It receives HTTP POST requests from e | `POST /trello/webhook` | Trello | HEAD/GET returns 200 for Trello's verification | | `POST /github/webhook` | GitHub | Injects `X-GitHub-Event` header into payload | | `POST /jira/webhook` | JIRA | HEAD/GET returns 200 for JIRA verification | +| `POST /linear/webhook` | Linear | HMAC-SHA256 via `linear-signature` header | | `POST /sentry/webhook/:projectId` | Sentry | Project ID in URL for unambiguous routing | | `GET /health` | Internal | Queue stats, active worker count | @@ -94,7 +95,7 @@ The router passes job data to workers via Docker container env vars: | Variable | Purpose | |----------|---------| | `JOB_ID` | Unique job identifier | -| `JOB_TYPE` | `trello`, `github`, `jira`, `sentry`, `manual-run`, `retry-run`, `debug-analysis` | +| `JOB_TYPE` | `trello`, `github`, `jira`, `linear`, `sentry`, `manual-run`, `retry-run`, `debug-analysis` | | `JOB_DATA` | JSON-encoded job payload | | `CASCADE_CREDENTIAL_KEYS` | Comma-separated list of credential env var names | | Individual credential vars | Pre-loaded project credentials (e.g., `GITHUB_TOKEN_IMPLEMENTER`) | @@ -106,6 +107,7 @@ type JobData = | TrelloJobData // Trello webhook payload | GitHubJobData // GitHub webhook payload | JiraJobData // JIRA webhook payload + | LinearJobData // Linear webhook payload | SentryJobData // Sentry webhook payload | ManualRunJobData // Dashboard-initiated run | RetryRunJobData // Retry a failed run diff --git a/docs/architecture/02-webhook-pipeline.md b/docs/architecture/02-webhook-pipeline.md index dfd929ed..17cedd11 100644 --- a/docs/architecture/02-webhook-pipeline.md +++ b/docs/architecture/02-webhook-pipeline.md @@ -1,6 +1,6 @@ # Webhook Pipeline -Webhooks from external providers (Trello, GitHub, JIRA, Sentry) are processed through a two-layer system: a **webhook handler factory** that handles HTTP concerns, and a **router platform adapter** that implements the business logic pipeline. +Webhooks from external providers (Trello, JIRA, Linear, GitHub, Sentry) are processed through a two-layer system: a **webhook handler factory** that handles HTTP concerns, and a **router platform adapter** that implements the business logic pipeline. ## Webhook Handler Factory @@ -16,7 +16,7 @@ Each webhook endpoint provides a `WebhookHandlerConfig`: ```typescript interface WebhookHandlerConfig { - source: string; // 'trello' | 'github' | 'jira' | 'sentry' + source: string; // 'trello' | 'github' | 'jira' | 'linear' | 'sentry' parsePayload: (c: Context) => ParseResult; verifySignature?: (ctx, rawBody, projectId?) => VerificationResult | null; processWebhook: (payload, eventType?, headers?) => Promise; @@ -37,6 +37,7 @@ The factory handles: | `parseGitHubPayload()` | JSON or form-encoded body | `X-GitHub-Event` header | | `parseTrelloPayload()` | JSON body | `action.type` field | | `parseJiraPayload()` | JSON body | `webhookEvent` field | +| `parseLinearPayload()` | JSON body | `type` field | | `parseSentryPayload()` | JSON body | `Sentry-Hook-Resource` header | ## Platform Adapters @@ -81,6 +82,7 @@ interface ParsedWebhookEvent { | `TrelloRouterAdapter` | `src/router/adapters/trello.ts` | `boardId` | | `GitHubRouterAdapter` | `src/router/adapters/github.ts` | `repoFullName` | | `JiraRouterAdapter` | `src/router/adapters/jira.ts` | JIRA project key | +| `LinearRouterAdapter` | `src/router/adapters/linear.ts` | Linear team ID | | `SentryRouterAdapter` | `src/router/adapters/sentry.ts` | CASCADE `projectId` (from URL) | ## The 12-Step Pipeline @@ -144,6 +146,7 @@ Each provider's verification function checks for a stored `webhook_secret` crede | GitHub | `X-Hub-Signature-256` | HMAC-SHA256 | | Trello | Custom verification | Trello-specific | | JIRA | `X-Hub-Signature` | HMAC-SHA256 | +| Linear | `linear-signature` | HMAC-SHA256 (hex, no prefix) | | Sentry | `Sentry-Hook-Signature` | HMAC-SHA256 | If no webhook secret is configured for a project, verification is skipped (returns `null`). diff --git a/docs/architecture/03-trigger-system.md b/docs/architecture/03-trigger-system.md index 54b2ba70..213448bb 100644 --- a/docs/architecture/03-trigger-system.md +++ b/docs/architecture/03-trigger-system.md @@ -39,7 +39,7 @@ interface TriggerHandler { ```typescript interface TriggerContext { project: ProjectConfig; - source: TriggerSource; // 'trello' | 'github' | 'jira' | 'sentry' + source: TriggerSource; // 'trello' | 'github' | 'jira' | 'linear' | 'sentry' payload: unknown; // Raw webhook payload personaIdentities?: PersonaIdentities; // GitHub bot identities } @@ -64,12 +64,16 @@ interface TriggerResult { ## Built-in Triggers -Registration happens in `src/triggers/builtins.ts`, which delegates to per-platform `register.ts` files: +Registration happens in `src/triggers/builtins.ts`. PM providers (Trello, JIRA, Linear) contribute triggers via the manifest registry; SCM and alerting providers use their own `register.ts` functions: ```typescript function registerBuiltInTriggers(registry: TriggerRegistry): void { - registerTrelloTriggers(registry); - registerJiraTriggers(registry); + // PM providers register via the manifest registry (spec 006/009 pattern) + for (const manifest of listPMProviders()) { + for (const handler of manifest.triggerHandlers) { + registry.register(handler); + } + } registerGitHubTriggers(registry); registerSentryTriggers(registry); } @@ -109,6 +113,14 @@ function registerBuiltInTriggers(registry: TriggerRegistry): void { | `PrReadyToMergeTrigger` | PR approved + checks pass | PM status update (no agent) | | `PrConflictDetectedTrigger` | Merge conflict on PR | `resolve-conflicts` | +### Linear triggers (`src/triggers/linear/`) + +| Handler | Event | Agent | +|---------|-------|-------| +| `LinearCommentMentionTrigger` | Bot @mentioned in issue comment | `respond-to-planning-comment` | +| `LinearStatusChangedTrigger` | Issue state transition | Per-status mapping | +| `LinearReadyToProcessLabelTrigger` | "cascade-ready" label added | `splitting` | + ### Sentry triggers (`src/triggers/sentry/`) | Handler | Event | Agent | diff --git a/docs/architecture/08-config-credentials.md b/docs/architecture/08-config-credentials.md index 700548c5..0acffb24 100644 --- a/docs/architecture/08-config-credentials.md +++ b/docs/architecture/08-config-credentials.md @@ -16,13 +16,14 @@ The config provider loads project configuration from the database with in-memory | `loadProjectConfigByBoardId(boardId)` | Trello board ID | `{ project, config }` | | `loadProjectConfigByRepo(repo)` | GitHub `owner/repo` | `{ project, config }` | | `loadProjectConfigByJiraProjectKey(key)` | JIRA project key | `{ project, config }` | +| `loadProjectConfigByLinearTeamId(teamId)` | Linear team ID | `{ project, config }` | | `loadProjectConfigById(id)` | CASCADE project ID | `{ project, config }` | ### Caching `src/config/configCache.ts` — in-memory cache with TTL populated at service startup. Caches: - Full config object -- Per-project lookups by board ID, repo, JIRA key +- Per-project lookups by board ID, repo, JIRA key, Linear team ID - Invalidated on config writes (via tRPC mutations) ## Config Schema @@ -104,6 +105,11 @@ await withTrelloCredentials({ apiKey, token }, async () => { await withJiraCredentials({ email, apiToken, baseUrl }, async () => { // All JIRA API calls use these credentials }); + +// Linear +await withLinearCredentials({ apiKey }, async () => { + // All Linear API calls use these credentials +}); ``` ## Credential Encryption diff --git a/docs/cascade-directory.md b/docs/cascade-directory.md index d875941e..cdabbb4f 100644 --- a/docs/cascade-directory.md +++ b/docs/cascade-directory.md @@ -155,6 +155,7 @@ FEATURE_FLAGS=new-parser,strict-validation ``` TRELLO_API_KEY, TRELLO_TOKEN, GITHUB_TOKEN, +LINEAR_API_KEY, LINEAR_WEBHOOK_SECRET, OPENROUTER_API_KEY, CASCADE_WORKSPACE_DIR, CASCADE_LOCAL_MODE, CASCADE_INTERACTIVE, CONFIG_PATH, PORT, LOG_LEVEL, LLMIST_LOG_FILE, LLMIST_LOG_TEE, diff --git a/docs/getting-started.md b/docs/getting-started.md index e678c8b3..79d38b22 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -264,6 +264,26 @@ node bin/cascade.js projects integration-set my-project \ --config '{"baseUrl":"https://yourorg.atlassian.net","projectKey":"PROJ","statuses":{"todo":"To Do","inProgress":"In Progress","inReview":"In Review"}}' ``` +### Linear + +1. Generate a **Personal API key** from https://linear.app/settings/api +2. (Optional) Create a webhook in your Linear workspace settings and note the signing secret + +```bash +# Store Linear credentials (project-scoped) +node bin/cascade.js projects credentials-set my-project --key LINEAR_API_KEY --value lin_api_... --name "Linear API Key" + +# Optional: webhook secret for signature verification +node bin/cascade.js projects credentials-set my-project --key LINEAR_WEBHOOK_SECRET --value ... --name "Linear Webhook Secret" + +# Configure the integration +# teamId: your Linear team UUID (find it via Settings > API or the team URL) +# statuses: map Cascade lifecycle stages to Linear workflow state IDs +node bin/cascade.js projects integration-set my-project \ + --category pm --provider linear \ + --config '{"teamId":"TEAM_UUID","statuses":{"todo":"STATE_UUID","inProgress":"STATE_UUID","done":"STATE_UUID"},"labels":{"readyToProcess":"LABEL_UUID","processing":"LABEL_UUID"}}' +``` + --- ## 9. Set Up Webhooks @@ -285,7 +305,7 @@ node bin/cascade.js webhooks create my-project \ --callback-url https://your-tunnel.ngrok.io ``` -This creates webhooks on GitHub (and Trello if configured) pointing to your Router. +This creates webhooks on GitHub (and Trello if configured) pointing to your Router. For Linear, create the webhook manually in your Linear workspace settings, pointing to `https://your-router-host/linear/webhook`. --- @@ -323,7 +343,7 @@ node bin/cascade.js projects trigger-discover --agent implementation ## 11. Test It -1. Create a card in your PM tool (Trello/Jira) with a clear description of what code change you want +1. Create a card in your PM tool (Trello/Jira/Linear) with a clear description of what code change you want 2. Move it to the status that triggers the implementation agent (or add the "Ready to Process" label) 3. Watch the dashboard — a new run should appear within seconds 4. The agent clones your repo, writes code, and opens a pull request