better-symphony 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +60 -0
- package/LICENSE +21 -0
- package/README.md +292 -0
- package/dist/web/app.css +2 -0
- package/dist/web/index.html +13 -0
- package/dist/web/main.js +235 -0
- package/package.json +62 -0
- package/src/agent/claude-runner.ts +576 -0
- package/src/agent/protocol.ts +2 -0
- package/src/agent/runner.ts +2 -0
- package/src/agent/session.ts +113 -0
- package/src/cli.ts +354 -0
- package/src/config/loader.ts +379 -0
- package/src/config/types.ts +382 -0
- package/src/index.ts +53 -0
- package/src/linear-cli.ts +414 -0
- package/src/logging/logger.ts +143 -0
- package/src/orchestrator/multi-orchestrator.ts +266 -0
- package/src/orchestrator/orchestrator.ts +1357 -0
- package/src/orchestrator/scheduler.ts +195 -0
- package/src/orchestrator/state.ts +201 -0
- package/src/prompts/github-system-prompt.md +51 -0
- package/src/prompts/linear-system-prompt.md +44 -0
- package/src/tracker/client.ts +577 -0
- package/src/tracker/github-issues-tracker.ts +280 -0
- package/src/tracker/github-pr-tracker.ts +298 -0
- package/src/tracker/index.ts +9 -0
- package/src/tracker/interface.ts +76 -0
- package/src/tracker/linear-tracker.ts +147 -0
- package/src/tracker/queries.ts +281 -0
- package/src/tracker/types.ts +125 -0
- package/src/tui/App.tsx +157 -0
- package/src/tui/LogView.tsx +120 -0
- package/src/tui/StatusBar.tsx +72 -0
- package/src/tui/TabBar.tsx +55 -0
- package/src/tui/sink.ts +47 -0
- package/src/tui/types.ts +6 -0
- package/src/tui/useOrchestrator.ts +244 -0
- package/src/web/server.ts +182 -0
- package/src/web/sink.ts +67 -0
- package/src/web-ui/App.tsx +60 -0
- package/src/web-ui/components/agent-table.tsx +57 -0
- package/src/web-ui/components/header.tsx +72 -0
- package/src/web-ui/components/log-stream.tsx +111 -0
- package/src/web-ui/components/retry-table.tsx +58 -0
- package/src/web-ui/components/stats-cards.tsx +142 -0
- package/src/web-ui/components/ui/badge.tsx +30 -0
- package/src/web-ui/components/ui/button.tsx +39 -0
- package/src/web-ui/components/ui/card.tsx +32 -0
- package/src/web-ui/globals.css +27 -0
- package/src/web-ui/index.html +13 -0
- package/src/web-ui/lib/use-sse.ts +98 -0
- package/src/web-ui/lib/utils.ts +25 -0
- package/src/web-ui/main.tsx +4 -0
- package/src/workspace/hooks.ts +97 -0
- package/src/workspace/manager.ts +211 -0
- package/src/workspace/render-hook.ts +13 -0
- package/workflows/dev.md +127 -0
- package/workflows/github-issues.md +107 -0
- package/workflows/pr-review.md +89 -0
- package/workflows/prd.md +170 -0
- package/workflows/ralph.md +95 -0
- package/workflows/smoke.md +66 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## What is Symphony
|
|
6
|
+
|
|
7
|
+
Symphony is a headless coding agent orchestrator. It polls issue trackers (Linear, GitHub Issues, GitHub PRs) for work items, dispatches Claude Code agents to complete them, and manages the full lifecycle from task selection through completion.
|
|
8
|
+
|
|
9
|
+
Workflows are defined as Markdown files with YAML frontmatter (config) + Liquid templates (agent prompts). Multiple workflows can run concurrently in one process.
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bun install # Install dependencies
|
|
15
|
+
bun run src/cli.ts # Start with TUI (auto-detects workflows/*.md)
|
|
16
|
+
bun run src/cli.ts -w workflows/dev.md # Run specific workflow(s)
|
|
17
|
+
bun run src/cli.ts --headless # Run without TUI
|
|
18
|
+
bun run src/cli.ts --web # Run with web dashboard (implies --headless)
|
|
19
|
+
bun run src/cli.ts --web --web-port 8080 # Web dashboard on custom port (default: 3000)
|
|
20
|
+
bun run src/cli.ts --dry-run # Preview rendered prompts, no agent launched
|
|
21
|
+
bun run --watch src/cli.ts # Dev mode with file watching
|
|
22
|
+
bun run src/linear-cli.ts # Standalone Linear CLI tool
|
|
23
|
+
tsc --noEmit # Type check (no build step needed; Bun runs TS directly)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Architecture
|
|
27
|
+
|
|
28
|
+
### Core Flow
|
|
29
|
+
|
|
30
|
+
1. **Tracker** polls for issues matching configured labels/states
|
|
31
|
+
2. **Orchestrator** claims an issue (atomic, prevents duplicates) and creates a per-issue **Workspace** via hooks (e.g., git clone)
|
|
32
|
+
3. **Config loader** renders the Liquid template with issue context to produce the agent prompt
|
|
33
|
+
4. **Claude runner** spawns `claude` CLI in the workspace, streaming `--output-format stream-json` events
|
|
34
|
+
5. On completion, labels are swapped to reflect status (`agent:dev` → `agent:dev:done` or `agent:dev:error`)
|
|
35
|
+
|
|
36
|
+
### Key Abstractions
|
|
37
|
+
|
|
38
|
+
- **Tracker interface** (`src/tracker/interface.ts`): Polymorphic abstraction over Linear (GraphQL), GitHub Issues (`gh` CLI), and GitHub PRs (`gh` CLI). Factory in `src/tracker/index.ts`.
|
|
39
|
+
- **Orchestrator** (`src/orchestrator/orchestrator.ts`): Single-workflow poll loop with concurrency control, retry queue, and token tracking. **MultiOrchestrator** coordinates multiple workflows sharing one Linear client.
|
|
40
|
+
- **Scheduler** (`src/orchestrator/scheduler.ts`): Manages poll intervals and `max_concurrent_agents` / `max_concurrent_agents_by_state` limits.
|
|
41
|
+
- **State** (`src/orchestrator/state.ts`): Tracks claims, running sessions, retries, and aggregate token usage.
|
|
42
|
+
- **Workspace manager** (`src/workspace/manager.ts`): Creates per-issue directories, runs `after_create`/`before_run` shell hooks with Liquid template support.
|
|
43
|
+
- **Claude runner** (`src/agent/claude-runner.ts`): Spawns Claude CLI, parses stream-json events for real-time status and token counts.
|
|
44
|
+
|
|
45
|
+
### Workflow Modes
|
|
46
|
+
|
|
47
|
+
- **default**: One agent per issue, runs to completion
|
|
48
|
+
- **ralph_loop**: Loops through subtasks, spawning a fresh agent per subtask with clean context
|
|
49
|
+
|
|
50
|
+
### Environment Variables
|
|
51
|
+
|
|
52
|
+
Agents receive `SYMPHONY_LINEAR` (path to Linear CLI), `SYMPHONY_WORKSPACE` (workspace path), `SYMPHONY_ISSUE_IDENTIFIER` (e.g., `SYM-123`), and `GH_REPO` (for GitHub trackers) in their environment.
|
|
53
|
+
|
|
54
|
+
## Tech Stack
|
|
55
|
+
|
|
56
|
+
- **Runtime**: Bun (executes TypeScript directly, no build step)
|
|
57
|
+
- **Language**: TypeScript 5.8 with strict mode
|
|
58
|
+
- **TUI**: React + Ink (terminal UI framework)
|
|
59
|
+
- **Templates**: LiquidJS for rendering workflow prompts with issue context
|
|
60
|
+
- **Config**: YAML frontmatter parsed from workflow Markdown files
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Sabatino Masala
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# Better Symphony
|
|
2
|
+
|
|
3
|
+
A headless coding agent orchestrator that polls issue trackers (Linear, GitHub Issues, GitHub PRs) for work items, dispatches AI agents (Claude Code), and manages the full development lifecycle.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Clone and install Symphony
|
|
9
|
+
git clone https://github.com/AugmentCo/better-symphony.git
|
|
10
|
+
cd better-symphony
|
|
11
|
+
bun install
|
|
12
|
+
|
|
13
|
+
# Optional: create a global alias so you can run `symphony` from anywhere
|
|
14
|
+
alias symphony="bun run $(pwd)/src/cli.ts"
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
> **Important:** Symphony is run from **your project's directory**, not from inside the `better-symphony` repo. Your project should have a `workflows/` folder containing your workflow `.md` files. Symphony auto-detects `workflows/*.md` in the current working directory.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
cd ~/your-project # Your project with a workflows/ directory
|
|
23
|
+
|
|
24
|
+
# Set your Linear API key
|
|
25
|
+
export LINEAR_API_KEY=lin_api_xxxxx
|
|
26
|
+
|
|
27
|
+
# Run all workflows in workflows/
|
|
28
|
+
symphony
|
|
29
|
+
|
|
30
|
+
# Or run specific workflow(s)
|
|
31
|
+
symphony -w workflows/dev.md
|
|
32
|
+
symphony -w workflows/prd.md workflows/dev.md workflows/ralph.md
|
|
33
|
+
|
|
34
|
+
# If you didn't set up the alias, use the full path:
|
|
35
|
+
bun run ~/path/to/better-symphony/src/cli.ts -w workflows/dev.md
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### CLI Flags
|
|
39
|
+
|
|
40
|
+
| Flag | Description |
|
|
41
|
+
|------|-------------|
|
|
42
|
+
| `-w <files>` | Run specific workflow file(s) |
|
|
43
|
+
| `--headless` | Run without the TUI |
|
|
44
|
+
| `--web` | Start web dashboard (implies `--headless`) |
|
|
45
|
+
| `--web-port <port>` | Web dashboard port (default: `3000`) |
|
|
46
|
+
| `--web-host <host>` | Web dashboard bind address (default: `0.0.0.0`) |
|
|
47
|
+
| `--dry-run` | Preview rendered prompts without launching agents |
|
|
48
|
+
|
|
49
|
+
### Project structure
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
your-project/
|
|
53
|
+
├── workflows/
|
|
54
|
+
│ ├── dev.md # Your workflow files
|
|
55
|
+
│ ├── prd.md
|
|
56
|
+
│ └── pr-review.md
|
|
57
|
+
├── src/ # Your project source code
|
|
58
|
+
└── ...
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## How It Works
|
|
62
|
+
|
|
63
|
+
Better Symphony uses **workflow files** (`workflows/*.md`) to define what the orchestrator does. Each workflow is a Markdown file with YAML frontmatter for configuration and a Liquid template for the agent prompt.
|
|
64
|
+
|
|
65
|
+
### Workflow Files
|
|
66
|
+
|
|
67
|
+
This repo includes example workflows you can copy into your project's `workflows/` directory:
|
|
68
|
+
|
|
69
|
+
- **`workflows/prd.md`** - PRD agent: analyzes issues and breaks complex ones into subtasks
|
|
70
|
+
- **`workflows/dev.md`** - Dev agent: implements tasks directly
|
|
71
|
+
- **`workflows/ralph.md`** - Ralph agent: loops through subtasks with fresh context per subtask
|
|
72
|
+
- **`workflows/pr-review.md`** - PR review agent: reviews GitHub PRs, runs tests, and posts review comments
|
|
73
|
+
- **`workflows/github-issues.md`** - GitHub Issues agent: implements tasks from GitHub Issues
|
|
74
|
+
|
|
75
|
+
Each workflow specifies which labels to watch for (e.g., `agent:dev`), so multiple workflows can run in parallel without conflicts.
|
|
76
|
+
|
|
77
|
+
### Source Code
|
|
78
|
+
|
|
79
|
+
- **`src/cli.ts`** - Entry point and argument parsing
|
|
80
|
+
- **`src/orchestrator/`** - Poll loop, scheduling, concurrency control, and multi-workflow coordination
|
|
81
|
+
- **`src/tracker/`** - Tracker implementations (Linear GraphQL, GitHub Issues, GitHub PRs via `gh` CLI)
|
|
82
|
+
- **`src/workspace/`** - Per-issue workspace creation/cleanup and shell hooks
|
|
83
|
+
- **`src/agent/`** - Agent harness (spawns Claude CLI, parses stream-json output)
|
|
84
|
+
- **`src/config/`** - YAML frontmatter + Liquid template parsing
|
|
85
|
+
- **`src/logging/`** - Structured logging
|
|
86
|
+
|
|
87
|
+
### Linear CLI
|
|
88
|
+
|
|
89
|
+
Better Symphony injects a `SYMPHONY_LINEAR` env var into every agent process, pointing to a bundled Linear CLI (`src/linear-cli.ts`). Agents use it to update issues, swap labels, create subtasks, and post comments without needing separate API keys.
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
bun $SYMPHONY_LINEAR get-issue SYM-123
|
|
93
|
+
bun $SYMPHONY_LINEAR update-issue SYM-123 --state "In Progress"
|
|
94
|
+
bun $SYMPHONY_LINEAR swap-label SYM-123 --remove "agent:dev" --add "agent:dev:done"
|
|
95
|
+
bun $SYMPHONY_LINEAR create-issue --parent SYM-123 --title "Implement feature X"
|
|
96
|
+
bun $SYMPHONY_LINEAR create-comment SYM-123 "Done implementing"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### GitHub CLI
|
|
100
|
+
|
|
101
|
+
For GitHub Issues integration, agents use the standard `gh` CLI directly. Better Symphony sets the `GH_REPO` environment variable automatically.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
gh issue view 123 --json number,title,body,state,labels,comments
|
|
105
|
+
gh issue create --title "Fix bug" --label "bug"
|
|
106
|
+
gh issue edit 123 --add-label "agent:dev:progress"
|
|
107
|
+
gh issue edit 123 --remove-label "agent:dev"
|
|
108
|
+
gh issue comment 123 --body "Done implementing"
|
|
109
|
+
gh issue close 123
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Workflow File Format
|
|
113
|
+
|
|
114
|
+
```yaml
|
|
115
|
+
---
|
|
116
|
+
tracker:
|
|
117
|
+
kind: linear
|
|
118
|
+
api_key: $LINEAR_API_KEY
|
|
119
|
+
project_slug: my-project
|
|
120
|
+
active_states: [Todo, In Progress]
|
|
121
|
+
terminal_states: [Done, Cancelled]
|
|
122
|
+
required_labels: [agent:dev]
|
|
123
|
+
excluded_labels: [agent:prd]
|
|
124
|
+
|
|
125
|
+
polling:
|
|
126
|
+
interval_ms: 30000
|
|
127
|
+
|
|
128
|
+
workspace:
|
|
129
|
+
root: ~/.symphony/workspaces
|
|
130
|
+
|
|
131
|
+
hooks:
|
|
132
|
+
after_create: |
|
|
133
|
+
git clone git@github.com:yourorg/repo.git .
|
|
134
|
+
bun install
|
|
135
|
+
before_run: |
|
|
136
|
+
git fetch origin main
|
|
137
|
+
git reset --hard origin/main
|
|
138
|
+
|
|
139
|
+
agent:
|
|
140
|
+
harness: claude
|
|
141
|
+
max_concurrent_agents: 2
|
|
142
|
+
max_turns: 20
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
You are working on **{{ issue.identifier }}**: {{ issue.title }}
|
|
146
|
+
|
|
147
|
+
## Description
|
|
148
|
+
{{ issue.description | default: "No description provided" }}
|
|
149
|
+
|
|
150
|
+
{% if issue.children.size > 0 %}
|
|
151
|
+
## Subtasks
|
|
152
|
+
{% for child in issue.children %}
|
|
153
|
+
- {{ child.identifier }}: {{ child.title }} ({{ child.state }})
|
|
154
|
+
{% endfor %}
|
|
155
|
+
{% endif %}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### GitHub Issues Tracker
|
|
159
|
+
|
|
160
|
+
For GitHub Issues, use `kind: github-issues`:
|
|
161
|
+
|
|
162
|
+
```yaml
|
|
163
|
+
---
|
|
164
|
+
tracker:
|
|
165
|
+
kind: github-issues
|
|
166
|
+
repo: owner/repo
|
|
167
|
+
active_states: [open]
|
|
168
|
+
terminal_states: [closed]
|
|
169
|
+
required_labels: [agent:dev]
|
|
170
|
+
excluded_labels: [agent:dev:done]
|
|
171
|
+
|
|
172
|
+
polling:
|
|
173
|
+
interval_ms: 30000
|
|
174
|
+
|
|
175
|
+
workspace:
|
|
176
|
+
root: ~/.symphony/workspaces
|
|
177
|
+
|
|
178
|
+
hooks:
|
|
179
|
+
after_create: |
|
|
180
|
+
git clone git@github.com:owner/repo.git .
|
|
181
|
+
bun install
|
|
182
|
+
before_run: |
|
|
183
|
+
git fetch origin main
|
|
184
|
+
git reset --hard origin/main
|
|
185
|
+
|
|
186
|
+
agent:
|
|
187
|
+
harness: claude
|
|
188
|
+
max_concurrent_agents: 2
|
|
189
|
+
max_turns: 20
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
You are working on **{{ issue.identifier }}** (#{{ issue.number }}): {{ issue.title }}
|
|
193
|
+
|
|
194
|
+
## Description
|
|
195
|
+
{{ issue.description | default: "No description provided" }}
|
|
196
|
+
|
|
197
|
+
When done, use `gh issue edit {{ issue.number }} --add-label "agent:dev:done"` to mark completion.
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### GitHub PR Tracker
|
|
201
|
+
|
|
202
|
+
For GitHub Pull Requests, use `kind: github-pr`:
|
|
203
|
+
|
|
204
|
+
```yaml
|
|
205
|
+
---
|
|
206
|
+
tracker:
|
|
207
|
+
kind: github-pr
|
|
208
|
+
repo: owner/repo
|
|
209
|
+
active_states: [open]
|
|
210
|
+
terminal_states: [closed, merged]
|
|
211
|
+
excluded_labels: [review:complete]
|
|
212
|
+
|
|
213
|
+
workspace:
|
|
214
|
+
root: ~/.symphony/workspaces
|
|
215
|
+
|
|
216
|
+
hooks:
|
|
217
|
+
after_create: |
|
|
218
|
+
git clone git@github.com:owner/repo.git .
|
|
219
|
+
before_run: |
|
|
220
|
+
git fetch origin
|
|
221
|
+
git checkout {{ issue.branch_name }}
|
|
222
|
+
git merge origin/main --no-edit || true
|
|
223
|
+
|
|
224
|
+
agent:
|
|
225
|
+
harness: claude
|
|
226
|
+
max_concurrent_agents: 1
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
You are reviewing **PR #{{ issue.number }}**: {{ issue.title }}
|
|
230
|
+
|
|
231
|
+
**Branch:** `{{ issue.branch_name }}` → `{{ issue.base_branch }}`
|
|
232
|
+
**Author:** {{ issue.author }}
|
|
233
|
+
**Files changed:** {{ issue.files_changed }}
|
|
234
|
+
|
|
235
|
+
## Description
|
|
236
|
+
{{ issue.body | default: "No description provided" }}
|
|
237
|
+
|
|
238
|
+
When done, use `gh pr edit {{ issue.number }} --add-label "review:complete"` to mark completion.
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
The GitHub PR tracker exposes additional template variables: `issue.branch_name`, `issue.base_branch`, `issue.author`, `issue.files_changed`, and `issue.comments`.
|
|
242
|
+
|
|
243
|
+
## Yolobox Support
|
|
244
|
+
|
|
245
|
+
Better Symphony has first-class support for [Yolobox](https://github.com/finbarr/yolobox), a Docker-based sandbox for running agents. When enabled, the agent binary is launched inside a Yolobox container.
|
|
246
|
+
|
|
247
|
+
```yaml
|
|
248
|
+
agent:
|
|
249
|
+
harness: claude # which agent to run: claude, codex, opencode
|
|
250
|
+
yolobox: true
|
|
251
|
+
yolobox_arguments: ["--claude-config"] # extra args passed to yolobox before the agent flags
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
This produces: `yolobox claude --claude-config -- -p "..." --output-format stream-json --verbose ...`
|
|
255
|
+
|
|
256
|
+
When yolobox is enabled, Symphony automatically:
|
|
257
|
+
- **Mounts** the Symphony source directory into the container (so `$SYMPHONY_LINEAR` resolves correctly)
|
|
258
|
+
- **Forwards** environment variables via `--env`: `SYMPHONY_LINEAR`, `SYMPHONY_WORKSPACE`, `SYMPHONY_ISSUE_ID`, `SYMPHONY_ISSUE_IDENTIFIER`, and `LINEAR_API_KEY`
|
|
259
|
+
|
|
260
|
+
Without `yolobox: true`, the harness binary is invoked directly.
|
|
261
|
+
|
|
262
|
+
## Labels
|
|
263
|
+
|
|
264
|
+
Each workflow watches for a specific label and adds status suffixes as it progresses:
|
|
265
|
+
|
|
266
|
+
| Label | Purpose |
|
|
267
|
+
|-------|---------|
|
|
268
|
+
| `agent:prd` | Break down issues into subtasks |
|
|
269
|
+
| `agent:dev` | Implement tasks directly |
|
|
270
|
+
| `agent:ralph` | Loop through subtasks with fresh context |
|
|
271
|
+
|
|
272
|
+
Status flow: `agent:dev` → `agent:dev:progress` → `agent:dev:done` (or `agent:dev:error`)
|
|
273
|
+
|
|
274
|
+
To retry a failed issue: remove the `:error` label and re-add the base label.
|
|
275
|
+
|
|
276
|
+
## Environment Variables
|
|
277
|
+
|
|
278
|
+
| Variable | Description |
|
|
279
|
+
|----------|-------------|
|
|
280
|
+
| `LINEAR_API_KEY` | Required for Linear tracker. Your Linear API key |
|
|
281
|
+
| `GH_REPO` | Required for GitHub trackers. Repository in `owner/repo` format |
|
|
282
|
+
| `SYMPHONY_LINEAR` | Injected into agents. Path to the Linear CLI |
|
|
283
|
+
| `SYMPHONY_WORKSPACE` | Injected into agents. Path to the issue workspace |
|
|
284
|
+
| `SYMPHONY_ISSUE_IDENTIFIER` | Injected into agents. e.g., `SYM-123` or `ISSUE-123` |
|
|
285
|
+
|
|
286
|
+
## License
|
|
287
|
+
|
|
288
|
+
MIT — see [LICENSE](LICENSE) for details.
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
Inspired by [openai/symphony](https://github.com/openai/symphony).
|
package/dist/web/app.css
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */
|
|
2
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-pan-x:initial;--tw-pan-y:initial;--tw-pinch-zoom:initial;--tw-space-y-reverse:0;--tw-space-x-reverse:0;--tw-divide-x-reverse:0;--tw-border-style:solid;--tw-divide-y-reverse:0;--tw-leading:initial;--tw-font-weight:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-blue-300:oklch(80.9% .105 251.813);--color-blue-400:oklch(70.7% .165 254.624);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-xs:20rem;--container-lg:32rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--radius-md:.375rem;--radius-lg:.5rem;--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--radius:.5rem;--color-background:oklch(14.5% 0 0);--color-foreground:oklch(98.5% 0 0);--color-card:oklch(17% 0 0);--color-card-foreground:oklch(98.5% 0 0);--color-muted:oklch(26.9% 0 0);--color-muted-foreground:oklch(70.8% 0 0);--color-border:oklch(30% 0 0);--color-accent:oklch(26.9% 0 0);--color-accent-foreground:oklch(98.5% 0 0);--color-destructive:oklch(57.7% .245 27.325);--color-primary:oklch(98.5% 0 0);--color-primary-foreground:oklch(20.5% 0 0);--color-secondary:oklch(26.9% 0 0);--color-secondary-foreground:oklch(98.5% 0 0);--color-ring:oklch(55.6% 0 0);--color-success:oklch(70% .2 145);--color-warning:oklch(75% .18 85)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.visible\!{visibility:visible!important}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.not-sr-only{clip-path:none;white-space:normal;width:auto;height:auto;margin:0;padding:0;position:static;overflow:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.end{inset-inline-end:var(--spacing)}.isolate{isolation:isolate}.isolation-auto{isolation:auto}.z-50{z-index:50}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-4{margin-inline:calc(var(--spacing) * 4)}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mr-2{margin-right:calc(var(--spacing) * 2)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-2{margin-left:calc(var(--spacing) * 2)}.block{display:block}.contents{display:contents}.flex{display:flex}.flow-root{display:flow-root}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.inline-grid{display:inline-grid}.inline-table{display:inline-table}.list-item{display:list-item}.table{display:table}.table-caption{display:table-caption}.table-cell{display:table-cell}.table-column{display:table-column}.table-column-group{display:table-column-group}.table-footer-group{display:table-footer-group}.table-header-group{display:table-header-group}.table-row{display:table-row}.table-row-group{display:table-row-group}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-2{height:calc(var(--spacing) * 2)}.h-3\.5{height:calc(var(--spacing) * 3.5)}.h-4{height:calc(var(--spacing) * 4)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-screen{height:100vh}.min-h-0{min-height:calc(var(--spacing) * 0)}.min-h-screen{min-height:100vh}.w-2{width:calc(var(--spacing) * 2)}.w-3\.5{width:calc(var(--spacing) * 3.5)}.w-9{width:calc(var(--spacing) * 9)}.w-\[70px\]{width:70px}.w-full{width:100%}.max-w-lg{max-width:var(--container-lg)}.max-w-xs{max-width:var(--container-xs)}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.border-collapse{border-collapse:collapse}.translate-none{translate:none}.scale-3d{scale:var(--tw-scale-x) var(--tw-scale-y) var(--tw-scale-z)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-spin{animation:var(--animate-spin)}.cursor-pointer{cursor:pointer}.touch-pinch-zoom{--tw-pinch-zoom:pinch-zoom;touch-action:var(--tw-pan-x,) var(--tw-pan-y,) var(--tw-pinch-zoom,)}.resize{resize:both}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-1\.5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1.5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1.5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-reverse>:not(:last-child)){--tw-space-y-reverse:1}:where(.space-x-reverse>:not(:last-child)){--tw-space-x-reverse:1}:where(.divide-x>:not(:last-child)){--tw-divide-x-reverse:0;border-inline-style:var(--tw-border-style);border-inline-start-width:calc(1px * var(--tw-divide-x-reverse));border-inline-end-width:calc(1px * calc(1 - var(--tw-divide-x-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-y-reverse>:not(:last-child)){--tw-divide-y-reverse:1}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:var(--radius)}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-s{border-start-start-radius:var(--radius);border-end-start-radius:var(--radius)}.rounded-ss{border-start-start-radius:var(--radius)}.rounded-e{border-start-end-radius:var(--radius);border-end-end-radius:var(--radius)}.rounded-se{border-start-end-radius:var(--radius)}.rounded-ee{border-end-end-radius:var(--radius)}.rounded-es{border-end-start-radius:var(--radius)}.rounded-t{border-top-left-radius:var(--radius);border-top-right-radius:var(--radius)}.rounded-l{border-top-left-radius:var(--radius);border-bottom-left-radius:var(--radius)}.rounded-tl{border-top-left-radius:var(--radius)}.rounded-r{border-top-right-radius:var(--radius);border-bottom-right-radius:var(--radius)}.rounded-tr{border-top-right-radius:var(--radius)}.rounded-b{border-bottom-right-radius:var(--radius);border-bottom-left-radius:var(--radius)}.rounded-br{border-bottom-right-radius:var(--radius)}.rounded-bl{border-bottom-left-radius:var(--radius)}.border{border-style:var(--tw-border-style);border-width:1px}.border-x{border-inline-style:var(--tw-border-style);border-inline-width:1px}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-s{border-inline-start-style:var(--tw-border-style);border-inline-start-width:1px}.border-e{border-inline-end-style:var(--tw-border-style);border-inline-end-width:1px}.border-bs{border-block-start-style:var(--tw-border-style);border-block-start-width:1px}.border-be{border-block-end-style:var(--tw-border-style);border-block-end-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-border{border-color:var(--color-border)}.border-transparent{border-color:#0000}.bg-background{background-color:var(--color-background)}.bg-black\/30{background-color:#0000004d}@supports (color:color-mix(in lab, red, red)){.bg-black\/30{background-color:color-mix(in oklab, var(--color-black) 30%, transparent)}}.bg-black\/60{background-color:#0009}@supports (color:color-mix(in lab, red, red)){.bg-black\/60{background-color:color-mix(in oklab, var(--color-black) 60%, transparent)}}.bg-card{background-color:var(--color-card)}.bg-destructive{background-color:var(--color-destructive)}.bg-muted{background-color:var(--color-muted)}.bg-muted\/20{background-color:#26262633}@supports (color:color-mix(in lab, red, red)){.bg-muted\/20{background-color:color-mix(in oklab, var(--color-muted) 20%, transparent)}}.bg-muted\/50{background-color:#26262680}@supports (color:color-mix(in lab, red, red)){.bg-muted\/50{background-color:color-mix(in oklab, var(--color-muted) 50%, transparent)}}.bg-primary{background-color:var(--color-primary)}.bg-secondary{background-color:var(--color-secondary)}.bg-success{background-color:var(--color-success)}.bg-success\/20{background-color:#30bd4433}@supports (color:color-mix(in lab, red, red)){.bg-success\/20{background-color:color-mix(in oklab, var(--color-success) 20%, transparent)}}.bg-transparent{background-color:#0000}.bg-warning\/20{background-color:#dba40033}@supports (color:color-mix(in lab, red, red)){.bg-warning\/20{background-color:color-mix(in oklab, var(--color-warning) 20%, transparent)}}.bg-repeat{background-repeat:repeat}.mask-no-clip{-webkit-mask-clip:no-clip;mask-clip:no-clip}.mask-repeat{-webkit-mask-repeat:repeat;mask-repeat:repeat}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-8{padding:calc(var(--spacing) * 8)}.px-1{padding-inline:calc(var(--spacing) * 1)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-0{padding-block:calc(var(--spacing) * 0)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-8{padding-block:calc(var(--spacing) * 8)}.pt-0{padding-top:calc(var(--spacing) * 0)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-none{--tw-leading:1;line-height:1}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.text-wrap{text-wrap:wrap}.break-all{word-break:break-all}.text-clip{text-overflow:clip}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.text-blue-400{color:var(--color-blue-400)}.text-card-foreground{color:var(--color-card-foreground)}.text-destructive{color:var(--color-destructive)}.text-foreground{color:var(--color-foreground)}.text-muted-foreground{color:var(--color-muted-foreground)}.text-primary-foreground{color:var(--color-primary-foreground)}.text-secondary-foreground{color:var(--color-secondary-foreground)}.text-success{color:var(--color-success)}.text-warning{color:var(--color-warning)}.text-white{color:var(--color-white)}.capitalize{text-transform:capitalize}.lowercase{text-transform:lowercase}.normal-case{text-transform:none}.uppercase{text-transform:uppercase}.italic{font-style:italic}.not-italic{font-style:normal}.diagonal-fractions{--tw-numeric-fraction:diagonal-fractions;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.lining-nums{--tw-numeric-figure:lining-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.oldstyle-nums{--tw-numeric-figure:oldstyle-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.proportional-nums{--tw-numeric-spacing:proportional-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.slashed-zero{--tw-slashed-zero:slashed-zero;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.stacked-fractions{--tw-numeric-fraction:stacked-fractions;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.normal-nums{font-variant-numeric:normal}.line-through{text-decoration-line:line-through}.no-underline{text-decoration-line:none}.overline{text-decoration-line:overline}.underline{text-decoration-line:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.subpixel-antialiased{-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.inset-ring{--tw-inset-ring-shadow:inset 0 0 0 1px var(--tw-inset-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-offset-background{--tw-ring-offset-color:var(--color-background)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.blur{--tw-blur:blur(8px);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.drop-shadow{--tw-drop-shadow-size:drop-shadow(0 1px 2px var(--tw-drop-shadow-color,#0000001a)) drop-shadow(0 1px 1px var(--tw-drop-shadow-color,#0000000f));--tw-drop-shadow:drop-shadow(0 1px 2px #0000001a) drop-shadow(0 1px 1px #0000000f);filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-grayscale{--tw-backdrop-grayscale:grayscale(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-invert{--tw-backdrop-invert:invert(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-sepia{--tw-backdrop-sepia:sepia(100%);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.\[agent\:dev\]{agent:dev}.\[agent\:prd\]{agent:prd}.\[review\:complete\]{review:complete}:where(.divide-x-reverse>:not(:last-child)){--tw-divide-x-reverse:1}.ring-inset{--tw-ring-inset:inset}.last\:border-0:last-child{border-style:var(--tw-border-style);border-width:0}@media (hover:hover){.hover\:border-muted-foreground:hover{border-color:var(--color-muted-foreground)}.hover\:bg-accent:hover{background-color:var(--color-accent)}.hover\:bg-destructive\/90:hover{background-color:#e40014e6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-destructive\/90:hover{background-color:color-mix(in oklab, var(--color-destructive) 90%, transparent)}}.hover\:bg-muted\/20:hover{background-color:#26262633}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/20:hover{background-color:color-mix(in oklab, var(--color-muted) 20%, transparent)}}.hover\:bg-muted\/30:hover{background-color:#2626264d}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/30:hover{background-color:color-mix(in oklab, var(--color-muted) 30%, transparent)}}.hover\:bg-muted\/40:hover{background-color:#26262666}@supports (color:color-mix(in lab, red, red)){.hover\:bg-muted\/40:hover{background-color:color-mix(in oklab, var(--color-muted) 40%, transparent)}}.hover\:bg-primary\/90:hover{background-color:#fafafae6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-primary\/90:hover{background-color:color-mix(in oklab, var(--color-primary) 90%, transparent)}}.hover\:bg-secondary\/80:hover{background-color:#262626cc}@supports (color:color-mix(in lab, red, red)){.hover\:bg-secondary\/80:hover{background-color:color-mix(in oklab, var(--color-secondary) 80%, transparent)}}.hover\:text-accent-foreground:hover{color:var(--color-accent-foreground)}.hover\:text-blue-300:hover{color:var(--color-blue-300)}.hover\:text-foreground:hover{color:var(--color-foreground)}}.focus-visible\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color:var(--color-ring)}.focus-visible\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}@media (min-width:40rem){.sm\:gap-4{gap:calc(var(--spacing) * 4)}.sm\:p-6{padding:calc(var(--spacing) * 6)}.sm\:px-6{padding-inline:calc(var(--spacing) * 6)}}}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,sans-serif}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-pan-x{syntax:"*";inherits:false}@property --tw-pan-y{syntax:"*";inherits:false}@property --tw-pinch-zoom{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-space-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-x-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Better Symphony</title>
|
|
7
|
+
<link rel="stylesheet" href="/app.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body class="bg-background text-foreground min-h-screen">
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
<script src="/main.js" type="module"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|