specrails-hub 1.54.1 → 1.54.2
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/README.md +78 -266
- package/client/dist/assets/{ActivityFeedPage-DzDSZ3Oa.js → ActivityFeedPage-D-6pkYiq.js} +1 -1
- package/client/dist/assets/AgentsPage-BmlV0tLU.js +86 -0
- package/client/dist/assets/AnalyticsPage-BLA44-85.js +1 -0
- package/client/dist/assets/{BarChart-BEDxM3GM.js → BarChart-erGMYSLM.js} +2 -2
- package/client/dist/assets/DocsDialog-BUuWLIDI.js +11 -0
- package/client/dist/assets/DocsPage-Dfnfe1v9.js +11 -0
- package/client/dist/assets/{ExportDropdown-C7gsLiLV.js → ExportDropdown-XowR3m8-.js} +1 -1
- package/client/dist/assets/HubAnalyticsPage-BzcWFJdx.js +1 -0
- package/client/dist/assets/{IntegrationsPage-CfggPfcY.js → IntegrationsPage-FoDqpmGE.js} +2 -2
- package/client/dist/assets/JobDetailPage-BEDHHbcD.js +16 -0
- package/client/dist/assets/JobsPage-D4m_aDkA.js +1 -0
- package/client/dist/assets/{dist-js-DTole_8A.js → dist-js-DI_gx9G3.js} +1 -1
- package/client/dist/assets/{dist-js-Cgr0qnSW.js → dist-js-YZ0tOOEy.js} +1 -1
- package/client/dist/assets/index-BC3icgTz.js +140 -0
- package/client/dist/assets/index-KaHNgvWR.css +2 -0
- package/client/dist/assets/{lib-DimzYFtz.js → lib-h5sZK86Y.js} +2 -2
- package/client/dist/assets/{useProjectCache-CttYIAMn.js → useProjectCache-T9bpe46I.js} +1 -1
- package/client/dist/index.html +2 -2
- package/docs/README.md +51 -0
- package/docs/cli.md +178 -0
- package/docs/creating-specs.md +176 -0
- package/docs/customizing.md +173 -0
- package/docs/getting-started.md +138 -0
- package/docs/internals/README.md +22 -0
- package/docs/internals/api-reference.md +461 -0
- package/docs/{engineering → internals}/architecture.md +1 -1
- package/docs/{engineering → internals}/configuration.md +13 -10
- package/docs/{product → internals}/openspec-workflow.md +2 -2
- package/docs/running-pipelines.md +211 -0
- package/docs/terminal.md +132 -0
- package/docs/tracking-cost.md +125 -0
- package/package.json +1 -1
- package/server/dist/ai-invocations.js +2 -0
- package/server/dist/chat-manager.js +42 -13
- package/server/dist/context-budget.js +113 -0
- package/server/dist/context-scope.js +272 -0
- package/server/dist/contract-refine-runner.js +485 -0
- package/server/dist/db.js +23 -13
- package/server/dist/docs-router.js +111 -31
- package/server/dist/explore-contract-refine.js +415 -0
- package/server/dist/explore-smash.js +450 -0
- package/server/dist/project-router.js +431 -21
- package/server/dist/smash-runner.js +638 -0
- package/server/dist/spending.js +63 -4
- package/server/dist/ticket-store.js +62 -2
- package/client/dist/assets/AgentsPage-NYn1AGPs.js +0 -86
- package/client/dist/assets/AnalyticsPage-DiiXVQAu.js +0 -1
- package/client/dist/assets/DocsDialog-flmuTVOr.js +0 -11
- package/client/dist/assets/DocsPage-C7r5RiZM.js +0 -11
- package/client/dist/assets/HubAnalyticsPage-Ca6I4pd4.js +0 -1
- package/client/dist/assets/JobDetailPage-BAlePf8f.js +0 -16
- package/client/dist/assets/JobsPage-Ut47fmBM.js +0 -1
- package/client/dist/assets/index-CkcoghGL.css +0 -2
- package/client/dist/assets/index-_3ZOPMZV.js +0 -133
- package/docs/engineering/api-reference.md +0 -267
- package/docs/engineering/rfcs/RFC-002-specrails-tech-api-v1.md +0 -167
- package/docs/engineering/rfcs/RFC-003-core-hub-sync.md +0 -227
- package/docs/engineering/spikes/specrails-hub-capabilities-spike.md +0 -299
- package/docs/general/getting-started.md +0 -129
- package/docs/general/platform-overview.md +0 -107
- package/docs/product/features.md +0 -162
- package/docs/product/tickets.md +0 -170
- package/docs/product/workflows.md +0 -107
- package/docs/terminal-panel.md +0 -75
- /package/docs/{operations/runbook.md → internals/operations-runbook.md} +0 -0
- /package/docs/{profiles-quick-start.md → internals/profiles.md} +0 -0
- /package/docs/{macos.md → platforms/macos.md} +0 -0
- /package/docs/{windows.md → platforms/windows.md} +0 -0
package/README.md
CHANGED
|
@@ -1,313 +1,125 @@
|
|
|
1
|
-
# specrails
|
|
1
|
+
# specrails-hub
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**The local dashboard for shipping software with AI agents.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
specrails-hub turns "I'll let Claude do it" into a workflow you can see, steer, and trust. Draft specs in conversation with Claude, drag them onto execution rails, and watch the pipeline ship — all from one window, on your laptop, with every cost tracked.
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
- **Live pipeline visualization** — see Architect, Developer, Reviewer, and Ship phases update in real-time
|
|
9
|
-
- **Streaming logs** — all `claude` CLI output streamed via WebSocket to the browser
|
|
10
|
-
- **Ticket panel** — visual interface for local tickets with List, Kanban, and Post-it views; real-time sync with CLI agents
|
|
11
|
-
- **Command launcher** — organized into Discovery (propose-spec, auto-propose specs, auto-select specs) and Delivery (implement, batch-implement) sections; other commands available in a collapsible group
|
|
12
|
-
- **Analytics** — cost, duration, token usage, and throughput metrics per project
|
|
13
|
-
- **Conversations** — full-page chat interface with Claude, scoped per project
|
|
14
|
-
- **Native desktop app (macOS/Windows/Linux via Tauri)** — installable app that bundles the server; macOS uses native traffic lights with a custom titlebar, Windows/Linux use a custom frameless titlebar
|
|
15
|
-
- **Demo mode for offline previews** — run the UI with fixture data, no server required
|
|
16
|
-
- **Persistent spec generation state across refreshes** — in-progress spec generation survives page reloads and project switches
|
|
17
|
-
- **`specrails-hub` CLI** — terminal bridge that auto-routes commands to the correct project
|
|
7
|
+
It's a local-first companion to [specrails-core](https://github.com/fjpulidop/specrails-core): one window for all your projects, one place to manage specs and pipelines, one place to see what AI cost you this week.
|
|
18
8
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- Node.js 20+ (specrails-core ≥ 4.2.0 dependency)
|
|
22
|
-
- `claude` CLI on your PATH ([Claude Code](https://claude.com/claude-code))
|
|
23
|
-
- At least one project with specrails-core installed (`npx specrails-core@latest init`)
|
|
24
|
-
- **Windows users:** see [docs/windows.md](docs/windows.md) for Windows 10/11 specifics (PowerShell ExecutionPolicy, SmartScreen warning, ARM64 emulation)
|
|
9
|
+
> 100% local. Single user. No accounts. No telemetry leaving your machine. Your code stays on your laptop unless **you** spawn an agent against it.
|
|
25
10
|
|
|
26
|
-
##
|
|
11
|
+
## What you can do with it
|
|
27
12
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
13
|
+
- **Draft a spec by talking to Claude** — open Explore, describe what you want; the live draft updates each turn. Save it as a draft and come back later, or commit when it looks right.
|
|
14
|
+
- **Generate a spec in one shot** — Quick mode for when you already know what you want. Optionally enrich it with a "Contract Layer" of names, shapes, invariants, and a file touch list.
|
|
15
|
+
- **Drag specs onto execution rails** — each rail is an independent lane. Run multiple specs in parallel, with different agent profiles per rail.
|
|
16
|
+
- **Compare two specs side by side** — drag any spec modal to the edge of the screen; a picker of your todo specs appears on the other side. Pick one and they live next to each other. Tablet-style.
|
|
17
|
+
- **Split a big epic** — SMASH a parent spec into a family of sub-specs in one click; the children carry short summaries on their cards.
|
|
18
|
+
- **Refine a spec in place** — *Continue Editing* reopens any draft / todo / backlog spec in Explore, with the original conversation resumed if there was one.
|
|
19
|
+
- **Track every AI cost** — every Claude CLI invocation across rails, Quick spec, Explore turns, and AI edits is recorded. The Analytics page shows your burn rate, top tickets, and lets you export CSV.
|
|
20
|
+
- **Customise everything** — three themes, font sizes, terminal preferences, agent profiles, plugin integrations (Serena bundled today).
|
|
31
21
|
|
|
32
|
-
##
|
|
22
|
+
## How specrails-hub looks
|
|
33
23
|
|
|
34
|
-
```bash
|
|
35
|
-
# Start the hub server
|
|
36
|
-
specrails-hub start
|
|
37
|
-
|
|
38
|
-
# Register a project
|
|
39
|
-
specrails-hub add /path/to/your/project
|
|
40
|
-
|
|
41
|
-
# Open in browser
|
|
42
|
-
open http://localhost:4200
|
|
43
24
|
```
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
~/.specrails/
|
|
60
|
-
hub.sqlite # project registry (name, path, slug)
|
|
61
|
-
manager.pid # server PID for clean shutdown
|
|
62
|
-
projects/
|
|
63
|
-
my-app/jobs.sqlite # isolated DB per project (jobs, events, chat)
|
|
64
|
-
api-srv/jobs.sqlite
|
|
25
|
+
┌──────────┬───────────────────────────────────────────────────┐
|
|
26
|
+
│ │ Dashboard · Jobs · Analytics · Agents · ⚙ │
|
|
27
|
+
│ Arc │ ──────────────────────────────────────────────── │
|
|
28
|
+
│ side- │ │
|
|
29
|
+
│ bar │ SpecsBoard │ Rails │
|
|
30
|
+
│ │ (your specs) │ (execution lanes) │
|
|
31
|
+
│ projects │ │ │
|
|
32
|
+
│ │ #1 Login flow ● │ ▶ Rail 1 #1 #2 │
|
|
33
|
+
│ + Add │ #2 Webhook retry │ [profile: default] │
|
|
34
|
+
│ │ #3 Cost limits ● │ │
|
|
35
|
+
│ │ │ ▶ Rail 2 running │
|
|
36
|
+
│ │ │ [profile: budget] │
|
|
37
|
+
│ ⚙ │ │ │
|
|
38
|
+
└──────────┴───────────────────────────────────────────────────┘
|
|
39
|
+
⌥ Terminal panel (Cmd+J)
|
|
65
40
|
```
|
|
66
41
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- **SQLite database** — jobs, events, chat conversations
|
|
70
|
-
- **QueueManager** — independent job queue (sequential within a project, parallel across projects)
|
|
71
|
-
- **ChatManager** — isolated Claude conversations
|
|
72
|
-
|
|
73
|
-
```
|
|
74
|
-
┌─────────────────────────────────────────────────────┐
|
|
75
|
-
│ Express Server (port 4200) │
|
|
76
|
-
│ │
|
|
77
|
-
│ ProjectRegistry │
|
|
78
|
-
│ ├── Project A → { db, queue, chat, cwd } │
|
|
79
|
-
│ ├── Project B → { db, queue, chat, cwd } │
|
|
80
|
-
│ └── Project C → { db, queue, chat, cwd } │
|
|
81
|
-
│ │
|
|
82
|
-
│ Routes: │
|
|
83
|
-
│ /api/hub/* → hub-level operations │
|
|
84
|
-
│ /api/projects/:id/* → project-scoped actions │
|
|
85
|
-
└─────────────────────────────────────────────────────┘
|
|
86
|
-
```
|
|
42
|
+
## Quick start
|
|
87
43
|
|
|
88
|
-
|
|
44
|
+
```bash
|
|
45
|
+
# 1. Install
|
|
46
|
+
npm install -g specrails-hub
|
|
89
47
|
|
|
90
|
-
|
|
91
|
-
specrails-hub
|
|
92
|
-
├── server/ → Express + WebSocket + SQLite (TypeScript, CommonJS)
|
|
93
|
-
├── client/ → React + Vite + Tailwind v4 (TypeScript, ESM)
|
|
94
|
-
├── cli/ → specrails-hub CLI bridge (TypeScript, CommonJS)
|
|
95
|
-
└── src-tauri/ → Tauri desktop shell (Rust + bundled server sidecar)
|
|
96
|
-
```
|
|
48
|
+
# 2. Start the hub
|
|
49
|
+
specrails-hub start
|
|
97
50
|
|
|
98
|
-
|
|
51
|
+
# 3. Add a project from the CLI…
|
|
52
|
+
specrails-hub add /path/to/your/project
|
|
99
53
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
│ specrails hub [my-app ●] [api-srv] [dashboard] [+]│
|
|
103
|
-
│ Home Analytics Conversations ⚙ │
|
|
104
|
-
│───────────────────────────────────────────────────────│
|
|
105
|
-
│ │
|
|
106
|
-
│ Command grid, recent jobs, pipeline phases │
|
|
107
|
-
│ │
|
|
108
|
-
└───────────────────────────────────────────────────────┘
|
|
54
|
+
# …or click "+ Add project" in the dashboard sidebar at
|
|
55
|
+
# http://127.0.0.1:4200
|
|
109
56
|
```
|
|
110
57
|
|
|
111
|
-
-
|
|
112
|
-
- **Home** — command grid (Discovery and Delivery sections), ticket panel, recent jobs, pipeline phase indicators
|
|
113
|
-
- **Tickets** — List, Kanban, and Post-it views of local tickets; real-time sync with CLI agents
|
|
114
|
-
- **Analytics** — cost and token metrics
|
|
115
|
-
- **Conversations** — Claude chat sessions scoped to the project
|
|
116
|
-
- **Settings** (gear icon) — global hub configuration, registered projects
|
|
58
|
+
If the project doesn't have specrails-core yet, the setup wizard walks you through installing it. One flow, no tier picker. Total time: about a minute on a warm cache.
|
|
117
59
|
|
|
118
|
-
|
|
60
|
+
**Prefer a desktop app?** Download a signed build for macOS or Windows from `https://specrails.dev/downloads/specrails-hub/latest/`. The desktop app bundles the server, so you don't need a separate `start` command.
|
|
119
61
|
|
|
120
|
-
|
|
62
|
+
## Prerequisites
|
|
121
63
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
| `specrails-hub status` | Show hub state and registered projects |
|
|
127
|
-
| `specrails-hub list` | List all registered projects |
|
|
128
|
-
| `specrails-hub add <path>` | Register a project |
|
|
129
|
-
| `specrails-hub remove <id>` | Unregister a project |
|
|
64
|
+
- **Node.js 20+**
|
|
65
|
+
- **`claude` CLI** ([Claude Code](https://claude.com/claude-code))
|
|
66
|
+
- **`git`**
|
|
67
|
+
- (Optional) **`uv`** if you want to use the Serena plugin
|
|
130
68
|
|
|
131
|
-
|
|
69
|
+
Set `ANTHROPIC_API_KEY` in your shell so the Claude CLI can authenticate. On macOS, the desktop app handles Homebrew/Volta/nvm paths automatically — see [docs/platforms/macos.md](docs/platforms/macos.md).
|
|
132
70
|
|
|
133
|
-
|
|
134
|
-
cd ~/repos/my-app
|
|
135
|
-
specrails-hub implement #42 # auto-detects project from CWD
|
|
136
|
-
specrails-hub product-backlog # routes to the correct project
|
|
137
|
-
specrails-hub "any raw prompt" # passes directly to claude
|
|
138
|
-
```
|
|
71
|
+
## Documentation
|
|
139
72
|
|
|
140
|
-
|
|
73
|
+
User guides:
|
|
141
74
|
|
|
142
|
-
|
|
75
|
+
| Guide | What it covers |
|
|
76
|
+
|-------|----------------|
|
|
77
|
+
| [Getting started](docs/getting-started.md) | Install, register a project, run your first pipeline |
|
|
78
|
+
| [Creating specs](docs/creating-specs.md) | Quick vs Explore, drafts, SMASH, Compare, Continue Editing |
|
|
79
|
+
| [Running pipelines](docs/running-pipelines.md) | Rails, jobs, agent profiles, plugins |
|
|
80
|
+
| [Tracking cost](docs/tracking-cost.md) | Analytics page, exports, per-ticket spending |
|
|
81
|
+
| [Customising the hub](docs/customizing.md) | Themes, settings, telemetry, kill switches |
|
|
82
|
+
| [Terminal panel](docs/terminal.md) | Keyboard shortcuts, shell integration, drag-and-drop |
|
|
83
|
+
| [CLI reference](docs/cli.md) | Every command grouped by task |
|
|
143
84
|
|
|
144
|
-
|
|
145
|
-
|------|-------------|
|
|
146
|
-
| `--port <n>` | Override default port (4200) |
|
|
147
|
-
| `--status` | Print hub/manager state |
|
|
148
|
-
| `--jobs` | Print recent job history |
|
|
149
|
-
| `--help` | Show usage |
|
|
85
|
+
Platform notes:
|
|
150
86
|
|
|
151
|
-
|
|
87
|
+
- [macOS](docs/platforms/macos.md) — GUI-launch PATH, broken-symlink detection
|
|
88
|
+
- [Windows](docs/platforms/windows.md) — installer formats, SmartScreen, ConPTY
|
|
152
89
|
|
|
153
|
-
|
|
154
|
-
[specrails-hub] running: /sr:implement #42
|
|
155
|
-
[specrails-hub] routing via hub → project my-app (a1b2c3d4)
|
|
156
|
-
... (live claude output) ...
|
|
157
|
-
[specrails-hub] done duration: 4m32s cost: $0.08 tokens: 12,400 exit: 0
|
|
158
|
-
```
|
|
90
|
+
Contributing or extending:
|
|
159
91
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
### Hub routes
|
|
163
|
-
|
|
164
|
-
| Method | Path | Description |
|
|
165
|
-
|--------|------|-------------|
|
|
166
|
-
| GET | `/api/hub/state` | Hub version, project count, uptime |
|
|
167
|
-
| GET | `/api/hub/projects` | List registered projects |
|
|
168
|
-
| POST | `/api/hub/projects` | Register a project (`{ path }`) |
|
|
169
|
-
| DELETE | `/api/hub/projects/:id` | Unregister a project |
|
|
170
|
-
| GET | `/api/hub/resolve?path=<p>` | Find project by filesystem path |
|
|
171
|
-
| GET | `/api/hub/settings` | Global settings |
|
|
172
|
-
| PUT | `/api/hub/settings` | Update global settings |
|
|
173
|
-
|
|
174
|
-
### Project-scoped routes
|
|
175
|
-
|
|
176
|
-
All under `/api/projects/:projectId/`:
|
|
177
|
-
|
|
178
|
-
| Method | Path | Description |
|
|
179
|
-
|--------|------|-------------|
|
|
180
|
-
| POST | `/spawn` | Launch a command |
|
|
181
|
-
| GET | `/jobs` | Job history |
|
|
182
|
-
| GET | `/jobs/:id` | Job detail |
|
|
183
|
-
| GET | `/analytics` | Cost and usage metrics |
|
|
184
|
-
| GET | `/config` | Available commands |
|
|
185
|
-
| POST | `/chat/conversations` | Create chat conversation |
|
|
186
|
-
| GET | `/chat/conversations` | List conversations |
|
|
187
|
-
| POST | `/hooks/events` | Pipeline phase notifications |
|
|
188
|
-
| GET | `/tickets` | List tickets (`?status=`, `?label=`, `?q=` filters supported) |
|
|
189
|
-
| GET | `/tickets/:id` | Get ticket by ID |
|
|
190
|
-
| POST | `/tickets` | Create ticket |
|
|
191
|
-
| PATCH | `/tickets/:id` | Update ticket fields |
|
|
192
|
-
| DELETE | `/tickets/:id` | Delete ticket |
|
|
193
|
-
| GET | `/integration-contract` | Read project integration-contract.json |
|
|
92
|
+
- [`docs/internals/`](docs/internals/) — architecture, REST reference, operations runbook, OpenSpec workflow, profile internals
|
|
194
93
|
|
|
195
94
|
## Development
|
|
196
95
|
|
|
197
96
|
```bash
|
|
198
97
|
git clone https://github.com/fjpulidop/specrails-hub.git
|
|
199
98
|
cd specrails-hub
|
|
200
|
-
npm install
|
|
201
|
-
cd client && npm install && cd ..
|
|
202
|
-
npm run dev
|
|
99
|
+
npm install # root deps (server + CLI)
|
|
100
|
+
cd client && npm install && cd .. # client deps (separate tree)
|
|
101
|
+
npm run dev # server (4200) + client (4201)
|
|
203
102
|
```
|
|
204
103
|
|
|
205
|
-
> **Note:** This repo has two separate `node_modules` trees — one at the root (server + CLI) and one inside `client/` (Vite + React). Both `npm install` calls are required. If you see `sh: tsc: command not found` during `npm run build`, it means one of them is missing.
|
|
206
|
-
|
|
207
104
|
| Script | Description |
|
|
208
105
|
|--------|-------------|
|
|
209
|
-
| `npm run dev` |
|
|
210
|
-
| `npm run
|
|
211
|
-
| `npm
|
|
212
|
-
| `npm run
|
|
213
|
-
| `npm run typecheck` | TypeScript check (server + client) |
|
|
214
|
-
| `npm test` | Run tests (vitest) |
|
|
215
|
-
|
|
216
|
-
### Project structure
|
|
217
|
-
|
|
218
|
-
```
|
|
219
|
-
specrails-hub/
|
|
220
|
-
├── server/
|
|
221
|
-
│ ├── index.ts # hub entry point
|
|
222
|
-
│ ├── hub-db.ts # hub SQLite (project registry)
|
|
223
|
-
│ ├── project-registry.ts # per-project context manager
|
|
224
|
-
│ ├── hub-router.ts # /api/hub/* routes
|
|
225
|
-
│ ├── project-router.ts # /api/projects/:id/* routes (includes ticket endpoints)
|
|
226
|
-
│ ├── ticket-store.ts # local-tickets.json read/write with file locking
|
|
227
|
-
│ ├── ticket-watcher.ts # chokidar watcher → WebSocket broadcast
|
|
228
|
-
│ ├── db.ts # per-project SQLite (jobs, events, chat)
|
|
229
|
-
│ ├── queue-manager.ts # job queue per project
|
|
230
|
-
│ ├── chat-manager.ts # Claude chat per project
|
|
231
|
-
│ ├── config.ts # command discovery
|
|
232
|
-
│ ├── hooks.ts # pipeline event handler
|
|
233
|
-
│ ├── analytics.ts # metrics aggregation
|
|
234
|
-
│ └── types.ts # shared TypeScript types (includes ticket WS messages)
|
|
235
|
-
├── client/
|
|
236
|
-
│ └── src/
|
|
237
|
-
│ ├── App.tsx
|
|
238
|
-
│ ├── components/
|
|
239
|
-
│ │ ├── TabBar.tsx # project tabs
|
|
240
|
-
│ │ ├── AddProjectDialog.tsx # register project modal
|
|
241
|
-
│ │ ├── WelcomeScreen.tsx # zero-state
|
|
242
|
-
│ │ ├── ProjectLayout.tsx # per-project wrapper
|
|
243
|
-
│ │ ├── ProjectNavbar.tsx # Home/Analytics/Conversations nav
|
|
244
|
-
│ │ ├── CommandGrid.tsx # command launcher
|
|
245
|
-
│ │ ├── TitleBar.tsx # custom titlebar (Windows/Linux frameless)
|
|
246
|
-
│ │ ├── ArcSidebar.tsx # collapsible Arc-style sidebar
|
|
247
|
-
│ │ ├── ProjectRightSidebar.tsx # project-level right panel
|
|
248
|
-
│ │ ├── TicketsSection.tsx # ticket panel container (view mode toggle)
|
|
249
|
-
│ │ ├── TicketListView.tsx # sortable table view
|
|
250
|
-
│ │ ├── TicketGridView.tsx # kanban drag-and-drop view
|
|
251
|
-
│ │ ├── TicketPostItView.tsx # sticky-note grid view
|
|
252
|
-
│ │ ├── TicketDetailModal.tsx # ticket editor modal
|
|
253
|
-
│ │ ├── CreateTicketModal.tsx # new ticket form
|
|
254
|
-
│ │ ├── TicketStatusIndicator.tsx # status dot, badge, border
|
|
255
|
-
│ │ ├── TicketContextMenu.tsx # right-click menu
|
|
256
|
-
│ │ └── ...
|
|
257
|
-
│ ├── hooks/
|
|
258
|
-
│ │ ├── useHub.tsx # hub state context
|
|
259
|
-
│ │ ├── useSpecGenTracker.tsx # spec generation state (persists via localStorage)
|
|
260
|
-
│ │ ├── useTickets.ts # ticket CRUD + WS subscription + toast/glow
|
|
261
|
-
│ │ ├── useChat.ts # chat operations
|
|
262
|
-
│ │ ├── usePipeline.ts # pipeline phases
|
|
263
|
-
│ │ └── useSharedWebSocket.tsx
|
|
264
|
-
│ ├── pages/
|
|
265
|
-
│ │ ├── DashboardPage.tsx
|
|
266
|
-
│ │ ├── AnalyticsPage.tsx
|
|
267
|
-
│ │ ├── ConversationsPage.tsx
|
|
268
|
-
│ │ ├── GlobalSettingsPage.tsx
|
|
269
|
-
│ │ └── JobDetailPage.tsx
|
|
270
|
-
│ └── lib/
|
|
271
|
-
│ ├── api.ts # dynamic API base routing
|
|
272
|
-
│ ├── pending-specs.ts # spec generation state persistence
|
|
273
|
-
│ └── route-memory.ts # per-project route save/restore
|
|
274
|
-
├── src-tauri/ # Tauri desktop shell
|
|
275
|
-
├── scripts/
|
|
276
|
-
│ ├── build-sidecar.mjs # build bundled server binary for Tauri
|
|
277
|
-
│ └── generate-icons.mjs # regenerate all icon sizes from SVG
|
|
278
|
-
├── cli/
|
|
279
|
-
│ └── specrails-hub.ts # CLI bridge
|
|
280
|
-
├── package.json
|
|
281
|
-
├── tsconfig.json
|
|
282
|
-
└── vitest.config.ts
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
## WebSocket
|
|
286
|
-
|
|
287
|
-
The server broadcasts events over a single WebSocket connection. All project-scoped messages include a `projectId` field — the client filters by active project.
|
|
106
|
+
| `npm run dev` | Server + client with hot reload |
|
|
107
|
+
| `npm run build` | Production build |
|
|
108
|
+
| `npm test` | vitest |
|
|
109
|
+
| `npm run tauri dev` | Run desktop app in dev mode |
|
|
288
110
|
|
|
289
|
-
|
|
290
|
-
|-------------|-------|-------------|
|
|
291
|
-
| `init` | project | Job started |
|
|
292
|
-
| `log` | project | Streaming log line |
|
|
293
|
-
| `phase` | project | Pipeline phase transition |
|
|
294
|
-
| `queue_update` | project | Queue state change |
|
|
295
|
-
| `ticket_created` | project | New ticket created (via API or CLI) |
|
|
296
|
-
| `ticket_updated` | project | Ticket updated; if `ticket.id === 0`, signals a full external file change |
|
|
297
|
-
| `ticket_deleted` | project | Ticket deleted |
|
|
298
|
-
| `hub.project_added` | hub | New project registered |
|
|
299
|
-
| `hub.project_removed` | hub | Project unregistered |
|
|
111
|
+
CI gates coverage hard: 70 % global, 80 % server, 80 % client. Local runs must clear the same bars.
|
|
300
112
|
|
|
301
|
-
## Security
|
|
113
|
+
## Security model
|
|
302
114
|
|
|
303
|
-
- Binds to `127.0.0.1`
|
|
304
|
-
- No authentication (single-user local tool)
|
|
305
|
-
-
|
|
306
|
-
-
|
|
115
|
+
- Binds to `127.0.0.1` only. **Do not expose to a network.**
|
|
116
|
+
- No authentication (single-user local tool).
|
|
117
|
+
- Parameterised SQL throughout.
|
|
118
|
+
- Reserved paths in your project (`.mcp.json`, `.specrails/plugins/state.json`, `.specrails/profiles/.user-preferred.json`) are mutated surgically — read, modify owned keys, atomic rename — so adding plugin N+1 never disturbs plugin N's state.
|
|
307
119
|
|
|
308
120
|
## Support
|
|
309
121
|
|
|
310
|
-
If specrails-hub saves you time, you can
|
|
122
|
+
If specrails-hub saves you time, you can buy me a coffee on [Ko-fi](https://ko-fi.com/D1D81Y002C). It funds development of the open-source ecosystem.
|
|
311
123
|
|
|
312
124
|
[](https://ko-fi.com/D1D81Y002C)
|
|
313
125
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./chunk-CilyBKbf.js";import{
|
|
1
|
+
import{r as e}from"./chunk-CilyBKbf.js";import{L as t,St as n,Vt as r,Z as i,_t as a,bt as o,ot as s,st as c,vt as l,xt as u,yt as d}from"./index-BC3icgTz.js";var f=l(`ban`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`M4.929 4.929 19.07 19.071`,key:`196cmz`}]]),p=e(r(),1);function m(e){let t=new Set;return e.filter(e=>{let n=`${e.type}:${e.jobId}`;return t.has(n)?!1:(t.add(n),!0)})}function h({activeProjectId:e,limit:t=50}){let[r,i]=(0,p.useState)([]),[a,s]=(0,p.useState)(!1),[c,l]=(0,p.useState)(!0),u=(0,p.useRef)(e);(0,p.useEffect)(()=>{u.current=e},[e]);let{registerHandler:d,unregisterHandler:f}=o();(0,p.useEffect)(()=>{if(!e){i([]),l(!0);return}let r=!1;s(!0),i([]),l(!0);async function a(){let e=n();try{let n=await fetch(`${e}/activity?limit=${t}`);if(!n.ok||r)return;let a=await n.json();if(r)return;i(a),l(a.length===t)}catch{}finally{r||s(!1)}}return a(),()=>{r=!0}},[e,t]);let h=(0,p.useCallback)(async()=>{if(a||!c)return;s(!0);let e=n();try{i(n=>{let r=n[n.length-1];if(!r)return n;let a=encodeURIComponent(r.timestamp);return fetch(`${e}/activity?limit=${t}&before=${a}`).then(e=>e.json()).then(e=>{i(t=>m([...t,...e])),l(e.length===t),s(!1)}).catch(()=>s(!1)),n})}catch{s(!1)}},[a,c,t]),g=(0,p.useCallback)(e=>{let t=e,n=u.current;if(!(!t||typeof t.type!=`string`)&&!(t.projectId&&t.projectId!==n)){if(t.type===`queue`&&Array.isArray(t.jobs)){let e=[];for(let n of t.jobs){let t=n.status===`completed`?`job_completed`:n.status===`failed`?`job_failed`:n.status===`canceled`?`job_canceled`:`job_started`;e.push({id:`${t}:${n.id}`,type:t,jobId:n.id,jobCommand:n.command??``,timestamp:n.startedAt??new Date().toISOString(),summary:`${t.replace(`_`,` `)}: ${n.command??``}`,costUsd:null})}e.length>0&&i(t=>m([...e,...t]))}if(t.type===`phase`&&t.phase&&t.state&&t.timestamp){let e={id:`phase:${t.phase}:${t.state}:${t.timestamp}`,type:`job_started`,jobId:`phase-${t.phase}`,jobCommand:`Phase: ${t.phase} → ${t.state}`,timestamp:t.timestamp,summary:`Phase ${t.phase} is ${t.state}`,costUsd:null};i(t=>m([e,...t]))}}},[]);return(0,p.useLayoutEffect)(()=>(d(`activity`,g),()=>f(`activity`)),[g,d,f]),{items:r,loading:a,hasMore:c,loadMore:h}}var g=u();function _(e){let t=Date.now()-new Date(e).getTime(),n=Math.floor(t/1e3);if(n<60)return`${n}s ago`;let r=Math.floor(n/60);if(r<60)return`${r}m ago`;let i=Math.floor(r/60);return i<24?`${i}h ago`:`${Math.floor(i/24)}d ago`}function v({type:e}){switch(e){case`job_completed`:return(0,g.jsx)(c,{className:`w-4 h-4 text-green-500 flex-shrink-0`});case`job_failed`:return(0,g.jsx)(s,{className:`w-4 h-4 text-red-500 flex-shrink-0`});case`job_canceled`:return(0,g.jsx)(f,{className:`w-4 h-4 text-muted-foreground flex-shrink-0`});default:return(0,g.jsx)(t,{className:`w-4 h-4 text-blue-500 flex-shrink-0`})}}function y(e){switch(e){case`job_completed`:return`Completed`;case`job_failed`:return`Failed`;case`job_canceled`:return`Canceled`;default:return`Started`}}function b(e){switch(e){case`job_completed`:return`text-green-500`;case`job_failed`:return`text-red-500`;case`job_canceled`:return`text-muted-foreground`;default:return`text-blue-500`}}function x(){let{activeProjectId:e}=d(),{items:t,loading:n,hasMore:r,loadMore:o}=h({activeProjectId:e}),s=(0,p.useRef)(null);return(0,p.useEffect)(()=>{let e=s.current;if(!e)return;let t=new IntersectionObserver(e=>{e[0].isIntersecting&&r&&!n&&o()},{threshold:.1});return t.observe(e),()=>t.disconnect()},[r,n,o]),(0,g.jsxs)(`div`,{className:`flex flex-col h-full overflow-hidden`,children:[(0,g.jsxs)(`div`,{className:`flex items-center gap-2 px-4 py-3 border-b border-border bg-background/50`,children:[(0,g.jsx)(a,{className:`w-4 h-4 text-muted-foreground`}),(0,g.jsx)(`h1`,{className:`text-sm font-medium`,children:`Activity`})]}),(0,g.jsxs)(`div`,{className:`flex-1 overflow-y-auto`,children:[n&&t.length===0?(0,g.jsx)(`div`,{className:`flex items-center justify-center h-32`,children:(0,g.jsx)(i,{className:`w-4 h-4 animate-spin text-muted-foreground`})}):t.length===0?(0,g.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-48 gap-2 text-muted-foreground`,children:[(0,g.jsx)(a,{className:`w-8 h-8 opacity-40`}),(0,g.jsx)(`p`,{className:`text-sm`,children:`No activity yet`}),(0,g.jsx)(`p`,{className:`text-xs opacity-70`,children:`Job events will appear here when jobs run`})]}):(0,g.jsx)(`ul`,{className:`divide-y divide-border/50`,children:t.map(e=>(0,g.jsxs)(`li`,{className:`flex items-center gap-3 px-4 py-2.5 hover:bg-accent/30 transition-colors`,children:[(0,g.jsx)(v,{type:e.type}),(0,g.jsxs)(`div`,{className:`flex-1 min-w-0`,children:[(0,g.jsx)(`p`,{className:`text-xs text-foreground truncate`,title:e.jobCommand,children:e.jobCommand}),(0,g.jsxs)(`div`,{className:`flex items-center gap-2 mt-0.5`,children:[(0,g.jsx)(`span`,{className:`text-xs font-medium ${b(e.type)}`,children:y(e.type)}),e.costUsd!=null&&(0,g.jsxs)(`span`,{className:`text-xs text-muted-foreground`,children:[`$`,e.costUsd.toFixed(4)]})]})]}),(0,g.jsx)(`span`,{className:`text-xs text-muted-foreground flex-shrink-0 tabular-nums`,children:_(e.timestamp)})]},`${e.type}:${e.jobId}`))}),(0,g.jsx)(`div`,{ref:s,className:`h-1`}),n&&t.length>0&&(0,g.jsx)(`div`,{className:`flex justify-center py-3`,children:(0,g.jsx)(i,{className:`w-4 h-4 animate-spin text-muted-foreground`})}),!r&&t.length>0&&(0,g.jsx)(`p`,{className:`text-center text-xs text-muted-foreground py-3`,children:`All activity loaded`})]})]})}export{x as default};
|