Skip to content

Commit ec5c292

Browse files
committed
release: v1.0.6 - PyPI distribution support
Added: - PyPI distribution: `pip install quorum-cli && quorum` works out of the box - GitHub Actions workflow for automated PyPI publishing (Trusted Publishing) - Frontend bundled into wheel automatically Fixed: - Startup spinner now shows immediately on all platforms - /help and /status modals can be closed with ESC key Changed: - Simplified launcher scripts with signal file coordination - Frontend signals readiness for seamless spinner-to-UI transition
1 parent eb81a46 commit ec5c292

14 files changed

Lines changed: 193 additions & 74 deletions

File tree

.github/workflows/publish.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
8+
jobs:
9+
publish:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
id-token: write # Required for trusted publishing
13+
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v5
19+
with:
20+
python-version: '3.12'
21+
22+
- name: Set up Node.js
23+
uses: actions/setup-node@v4
24+
with:
25+
node-version: '20'
26+
cache: 'npm'
27+
cache-dependency-path: frontend/package-lock.json
28+
29+
- name: Build frontend
30+
run: |
31+
cd frontend
32+
npm ci
33+
npm run build
34+
35+
- name: Install build tools
36+
run: pip install build
37+
38+
- name: Build package
39+
run: python -m build
40+
41+
- name: Publish to PyPI
42+
uses: pypa/gh-action-pypi-publish@release/v1

CHANGELOG.md

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
<!-- Changes after 1.0.5 release go here -->
10+
<!-- Changes after 1.0.6 release go here -->
11+
12+
---
13+
14+
## [1.0.6] - 2025-12-13
15+
16+
### Added
17+
18+
- **PyPI Distribution** - Install Quorum with a single command
19+
- `pip install quorum-cli && quorum` now works out of the box
20+
- Frontend is bundled into the wheel automatically
21+
- GitHub Actions workflow for automated PyPI publishing with Trusted Publishing
22+
23+
### Fixed
24+
25+
- **Startup Spinner** - Animated loading spinner now shows immediately on all platforms
26+
- Works consistently for both pip install and development mode
27+
- Signal file coordination between launcher and frontend
28+
- **Modal ESC Handling** - `/help` and `/status` modals can now be closed with ESC key
29+
30+
### Changed
31+
32+
- Simplified launcher scripts (removed signal file complexity from user-facing code)
33+
- Frontend now signals readiness to launcher for seamless spinner-to-UI transition
1134

1235
---
1336

@@ -185,7 +208,9 @@ User cache (`~/.quorum/`):
185208

186209
---
187210

188-
[Unreleased]: https://github.com/Detrol/quorum-cli/compare/v1.0.4...HEAD
211+
[Unreleased]: https://github.com/Detrol/quorum-cli/compare/v1.0.6...HEAD
212+
[1.0.6]: https://github.com/Detrol/quorum-cli/compare/v1.0.5...v1.0.6
213+
[1.0.5]: https://github.com/Detrol/quorum-cli/compare/v1.0.4...v1.0.5
189214
[1.0.4]: https://github.com/Detrol/quorum-cli/compare/v1.0.3...v1.0.4
190215
[1.0.3]: https://github.com/Detrol/quorum-cli/compare/v1.0.2...v1.0.3
191216
[1.0.2]: https://github.com/Detrol/quorum-cli/compare/v1.0.1...v1.0.2

README.md

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,30 @@
1919

2020
Multi-agent AI discussion system for structured debates. Ask multiple AI models (Claude, GPT, Gemini, Grok, and local models via Ollama) a question and let them debate, brainstorm, or deliberate using seven different methods.
2121

22-
## Requirements
22+
## Quick Install (pip)
23+
24+
```bash
25+
pip install quorum-cli
26+
quorum
27+
```
28+
29+
That's it! Configure your API keys on first run or create a `.env` file.
30+
31+
**Requirements:** Python 3.11+, Node.js 18+
32+
33+
---
34+
35+
## Development Installation
36+
37+
For contributors or those who want the latest changes:
38+
39+
### Requirements
2340

2441
- Python 3.11 or higher
2542
- Node.js 18 or higher
2643
- npm
2744
- [uv](https://github.com/astral-sh/uv) (auto-installed if missing)
2845

29-
## Installation
30-
3146
### Linux / macOS
3247

3348
```bash

frontend/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "quorum-frontend",
3-
"version": "1.0.5",
3+
"version": "1.0.6",
44
"description": "Terminal UI for Quorum multi-agent consensus system",
55
"license": "BSL-1.1",
66
"type": "module",

frontend/src/App.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -158,15 +158,16 @@ export function App() {
158158
setMaxTurns(userSettings.max_turns);
159159
}
160160

161-
// Settings loaded - show UI
162-
setBackendReady(true);
163-
164-
// Signal shell spinner to stop and clear screen for UI
161+
// Signal launcher spinner to stop and wait briefly for it to clear
165162
const signalFile = process.env.QUORUM_SIGNAL_FILE;
166163
if (signalFile) {
167-
writeFileSync(signalFile, "ready");
164+
try { writeFileSync(signalFile, "ready"); } catch {}
165+
await new Promise(r => setTimeout(r, 100)); // Let spinner clear itself
168166
}
169-
process.stdout.write('\x1Bc');
167+
168+
// Clear screen fully (screen + scrollback + cursor home)
169+
process.stdout.write('\x1B[2J\x1B[3J\x1B[H');
170+
setBackendReady(true);
170171

171172
// Mark cached validated models immediately
172173
const cachedValidated = new Set(modelsResult.validated || []);
@@ -697,7 +698,7 @@ export function App() {
697698
}
698699
});
699700

700-
// Render nothing while backend starts (shell spinner handles this)
701+
// Render nothing while backend starts (launcher handles spinner)
701702
if (!backendReady) {
702703
return null;
703704
}

frontend/src/components/Help.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,18 @@
33
*/
44

55
import React from "react";
6-
import { Box, Text } from "ink";
6+
import { Box, Text, useInput } from "ink";
77
import { t } from "../i18n/index.js";
8+
import { useStore } from "../store/index.js";
89

910
export function Help() {
11+
const { setShowHelp } = useStore();
12+
13+
useInput((input, key) => {
14+
if (key.escape) {
15+
setShowHelp(false);
16+
}
17+
});
1018
return (
1119
<Box
1220
flexDirection="column"

frontend/src/components/Status.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44

55
import React from "react";
6-
import { Box, Text } from "ink";
6+
import { Box, Text, useInput } from "ink";
77
import { useStore } from "../store/index.js";
88
import { getModelDisplayName } from "./Message.js";
99
import { t } from "../i18n/index.js";
@@ -15,8 +15,15 @@ export function Status() {
1515
discussionMethod,
1616
synthesizerMode,
1717
maxTurns,
18+
setShowStatus,
1819
} = useStore();
1920

21+
useInput((input, key) => {
22+
if (key.escape) {
23+
setShowStatus(false);
24+
}
25+
});
26+
2027
const modelNames = selectedModels.map((id) =>
2128
getModelDisplayName(id, availableModels)
2229
);

frontend/src/ipc/client.ts

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,8 @@
55
import { spawn, ChildProcess } from "node:child_process";
66
import { createInterface, Interface } from "node:readline";
77
import { EventEmitter } from "node:events";
8-
import { join, dirname } from "node:path";
9-
import { fileURLToPath } from "node:url";
108
import { v4 as uuidv4 } from "uuid";
119

12-
// Get project root directory from this file's location
13-
// This file is at: frontend/dist/ipc/client.js (after compilation)
14-
// Project root is 3 levels up: ../../../
15-
const __filename = fileURLToPath(import.meta.url);
16-
const __dirname = dirname(__filename);
17-
const PROJECT_ROOT = join(__dirname, "..", "..", "..");
18-
1910
import type {
2011
JsonRpcRequest,
2112
JsonRpcResponse,
@@ -55,17 +46,9 @@ export class BackendClient extends EventEmitter {
5546
constructor() {
5647
super();
5748

58-
// Use venv Python directly - bypasses uv entirely
59-
// PROJECT_ROOT is calculated from this file's location, not cwd
60-
// This ensures it works regardless of where the user runs quorum from
61-
62-
// Platform-specific venv Python path
63-
const isWindows = process.platform === "win32";
64-
const venvPython = isWindows
65-
? join(PROJECT_ROOT, ".venv", "Scripts", "python.exe")
66-
: join(PROJECT_ROOT, ".venv", "bin", "python");
67-
68-
this.pythonCommand = venvPython;
49+
// Python tells us where it is via QUORUM_PYTHON env var (set by main.py)
50+
// This works for both dev mode and pip install
51+
this.pythonCommand = process.env.QUORUM_PYTHON || "python";
6952
this.pythonArgs = ["-m", "quorum", "--ipc"];
7053
}
7154

@@ -83,8 +66,6 @@ export class BackendClient extends EventEmitter {
8366

8467
this.process = spawn(this.pythonCommand, this.pythonArgs, {
8568
stdio: ["pipe", "pipe", "pipe"],
86-
cwd: PROJECT_ROOT,
87-
env: { ...process.env, PYTHONPATH: join(PROJECT_ROOT, "src") },
8869
});
8970

9071
this.process.on("error", (err) => {

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ path = "src/quorum/constants.py"
5858
[tool.hatch.build.targets.wheel]
5959
packages = ["src/quorum"]
6060

61+
[tool.hatch.build.targets.wheel.force-include]
62+
"frontend/dist" = "quorum/_frontend"
63+
6164
[tool.pytest.ini_options]
6265
asyncio_mode = "auto"
6366
testpaths = ["tests", "src"]

0 commit comments

Comments
 (0)