bumblebee-cli 0.1.0 → 0.2.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.
Files changed (38) hide show
  1. package/README.md +49 -47
  2. package/bin/bb.mjs +132 -102
  3. package/package.json +28 -28
  4. package/python/bb_cli/__main__.py +3 -0
  5. package/python/bb_cli/api_client.py +7 -0
  6. package/python/bb_cli/commands/agent.py +2287 -1030
  7. package/python/bb_cli/commands/auth.py +79 -79
  8. package/python/bb_cli/commands/board.py +47 -47
  9. package/python/bb_cli/commands/comment.py +34 -34
  10. package/python/bb_cli/commands/daemon.py +153 -0
  11. package/python/bb_cli/commands/init.py +83 -62
  12. package/python/bb_cli/commands/item.py +192 -192
  13. package/python/bb_cli/commands/project.py +175 -111
  14. package/python/bb_cli/config.py +136 -136
  15. package/python/bb_cli/main.py +44 -44
  16. package/python/bb_cli/progress.py +117 -0
  17. package/python/bb_cli/streaming.py +168 -0
  18. package/python/pyproject.toml +1 -1
  19. package/python/{bb_cli/bumblebee_cli.egg-info/requires.txt → requirements.txt} +4 -4
  20. package/scripts/build.sh +20 -20
  21. package/scripts/postinstall.mjs +146 -121
  22. package/python/bb_cli/bumblebee_cli.egg-info/PKG-INFO +0 -9
  23. package/python/bb_cli/bumblebee_cli.egg-info/SOURCES.txt +0 -21
  24. package/python/bb_cli/bumblebee_cli.egg-info/dependency_links.txt +0 -1
  25. package/python/bb_cli/bumblebee_cli.egg-info/entry_points.txt +0 -2
  26. package/python/bb_cli/bumblebee_cli.egg-info/top_level.txt +0 -5
  27. package/python/bb_cli/commands/__pycache__/__init__.cpython-313.pyc +0 -0
  28. package/python/bb_cli/commands/__pycache__/agent.cpython-313.pyc +0 -0
  29. package/python/bb_cli/commands/__pycache__/auth.cpython-313.pyc +0 -0
  30. package/python/bb_cli/commands/__pycache__/board.cpython-313.pyc +0 -0
  31. package/python/bb_cli/commands/__pycache__/comment.cpython-313.pyc +0 -0
  32. package/python/bb_cli/commands/__pycache__/init.cpython-313.pyc +0 -0
  33. package/python/bb_cli/commands/__pycache__/item.cpython-313.pyc +0 -0
  34. package/python/bb_cli/commands/__pycache__/label.cpython-313.pyc +0 -0
  35. package/python/bb_cli/commands/__pycache__/project.cpython-313.pyc +0 -0
  36. package/python/bb_cli/commands/__pycache__/sprint.cpython-313.pyc +0 -0
  37. package/python/bb_cli/commands/__pycache__/story.cpython-313.pyc +0 -0
  38. package/python/bb_cli/commands/__pycache__/task.cpython-313.pyc +0 -0
@@ -0,0 +1,117 @@
1
+ """Rich Live progress tracker for batch agent operations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ import threading
7
+
8
+ from rich.live import Live
9
+ from rich.table import Table
10
+ from rich.text import Text
11
+
12
+
13
+ class AgentProgressTracker:
14
+ """Thread-safe progress tracker with Rich Live table display.
15
+
16
+ Usage:
17
+ with AgentProgressTracker() as tracker:
18
+ # From worker threads:
19
+ tracker.update("BB-42", "execute", "running", "Modifying auth.py")
20
+ tracker.complete("BB-42", True, "Done")
21
+ """
22
+
23
+ def __init__(self):
24
+ self._data: dict[str, dict] = {}
25
+ self._lock = threading.Lock()
26
+ self._start_time = time.monotonic()
27
+ self._completed = 0
28
+ self._total = 0
29
+ self._live: Live | None = None
30
+
31
+ def register(self, item_key: str):
32
+ """Register an item before work starts."""
33
+ with self._lock:
34
+ self._data[item_key] = {
35
+ "phase": "pending",
36
+ "status": "waiting",
37
+ "last_line": "",
38
+ "start": time.monotonic(),
39
+ }
40
+ self._total += 1
41
+
42
+ def update(self, item_key: str, phase: str, status: str, last_line: str):
43
+ """Update progress for an item (called from worker threads)."""
44
+ with self._lock:
45
+ if item_key not in self._data:
46
+ self._data[item_key] = {"start": time.monotonic()}
47
+ self._data[item_key].update({
48
+ "phase": phase,
49
+ "status": status,
50
+ "last_line": last_line,
51
+ })
52
+
53
+ def complete(self, item_key: str, success: bool, message: str = ""):
54
+ """Mark an item as completed."""
55
+ with self._lock:
56
+ status = "done" if success else "failed"
57
+ if item_key in self._data:
58
+ self._data[item_key].update({
59
+ "status": status,
60
+ "last_line": message[:60],
61
+ })
62
+ self._completed += 1
63
+
64
+ def _build_table(self) -> Table:
65
+ """Build the Rich table (called on each Live refresh)."""
66
+ table = Table(show_header=True, header_style="bold cyan", expand=True)
67
+ table.add_column("Item", style="bold", width=12)
68
+ table.add_column("Phase", width=14)
69
+ table.add_column("Status", width=10)
70
+ table.add_column("Last Output", ratio=1)
71
+
72
+ with self._lock:
73
+ for key, info in self._data.items():
74
+ status = info.get("status", "?")
75
+ phase = info.get("phase", "?")
76
+ last_line = info.get("last_line", "")
77
+
78
+ # Color status
79
+ if status == "running":
80
+ status_text = Text(status, style="yellow")
81
+ elif status == "done":
82
+ status_text = Text(status, style="green")
83
+ elif status == "failed":
84
+ status_text = Text(status, style="red")
85
+ else:
86
+ status_text = Text(status, style="dim")
87
+
88
+ table.add_row(key, phase, status_text, Text(last_line, overflow="ellipsis"))
89
+
90
+ elapsed = time.monotonic() - self._start_time
91
+ mins, secs = divmod(int(elapsed), 60)
92
+
93
+ table.caption = f"Elapsed: {mins}m {secs:02d}s | {self._completed}/{self._total} complete | Ctrl+C to abort"
94
+ return table
95
+
96
+ def __enter__(self):
97
+ self._live = Live(self._build_table(), refresh_per_second=2)
98
+ self._live.__enter__()
99
+ # Start refresh loop
100
+ self._refresh_thread = threading.Thread(target=self._refresh_loop, daemon=True)
101
+ self._stop_refresh = threading.Event()
102
+ self._refresh_thread.start()
103
+ return self
104
+
105
+ def __exit__(self, *args):
106
+ self._stop_refresh.set()
107
+ self._refresh_thread.join(timeout=2)
108
+ if self._live:
109
+ self._live.update(self._build_table())
110
+ self._live.__exit__(*args)
111
+
112
+ def _refresh_loop(self):
113
+ """Refresh the Live display periodically."""
114
+ while not self._stop_refresh.is_set():
115
+ if self._live:
116
+ self._live.update(self._build_table())
117
+ self._stop_refresh.wait(0.5)
@@ -0,0 +1,168 @@
1
+ """Buffered agent output streaming with local log fallback."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import queue
7
+ import threading
8
+ import time
9
+ from pathlib import Path
10
+
11
+ from .api_client import api_post
12
+
13
+
14
+ def _logs_dir() -> Path:
15
+ d = Path.home() / ".bumblebee" / "logs"
16
+ d.mkdir(parents=True, exist_ok=True)
17
+ return d
18
+
19
+
20
+ class AgentStreamer:
21
+ """Thread-safe buffered relay for Claude CLI stream-json output.
22
+
23
+ - Reads JSON lines via feed()
24
+ - Writes each line to a local log file immediately (safety net)
25
+ - Batches events and POSTs to /relay-batch every FLUSH_INTERVAL_S or BATCH_SIZE events
26
+ - Extracts text blocks for summary
27
+ """
28
+
29
+ BATCH_SIZE = 20
30
+ FLUSH_INTERVAL_S = 0.5
31
+
32
+ def __init__(self, session_id: int | str):
33
+ self._session_id = session_id
34
+ self._queue: queue.Queue[dict] = queue.Queue()
35
+ self._done = threading.Event()
36
+ self._text_blocks: list[str] = []
37
+ self._log_path = _logs_dir() / f"session-{session_id}.jsonl"
38
+ self._log_file = open(self._log_path, "a", encoding="utf-8")
39
+ self._sender_thread: threading.Thread | None = None
40
+ self._callback: callable | None = None
41
+
42
+ @property
43
+ def text_blocks(self) -> list[str]:
44
+ return self._text_blocks
45
+
46
+ @property
47
+ def log_path(self) -> Path:
48
+ return self._log_path
49
+
50
+ def set_callback(self, cb: callable):
51
+ """Set a callback(payload: dict, text: str|None) for each event."""
52
+ self._callback = cb
53
+
54
+ def start(self):
55
+ """Start the background sender thread."""
56
+ self._sender_thread = threading.Thread(
57
+ target=self._sender_loop, daemon=True, name=f"streamer-{self._session_id}"
58
+ )
59
+ self._sender_thread.start()
60
+
61
+ def feed(self, line: str):
62
+ """Feed a raw line from Claude CLI stdout."""
63
+ line = line.strip()
64
+ if not line:
65
+ return
66
+
67
+ try:
68
+ payload = json.loads(line)
69
+ except json.JSONDecodeError:
70
+ return
71
+
72
+ # Write to log file immediately
73
+ self._log_file.write(line + "\n")
74
+ self._log_file.flush()
75
+
76
+ # Extract text blocks
77
+ text = None
78
+ if payload.get("type") == "assistant":
79
+ for block in payload.get("content", []):
80
+ if block.get("type") == "text":
81
+ text = block["text"]
82
+ self._text_blocks.append(text)
83
+
84
+ # Notify callback (for terminal display, progress tracker, etc.)
85
+ if self._callback:
86
+ try:
87
+ self._callback(payload, text)
88
+ except Exception:
89
+ pass
90
+
91
+ # Enqueue for batch relay
92
+ self._queue.put(payload)
93
+
94
+ def stop(self) -> list[str]:
95
+ """Signal completion, flush remaining events, return text blocks."""
96
+ self._done.set()
97
+ if self._sender_thread:
98
+ self._sender_thread.join(timeout=10)
99
+ self._log_file.close()
100
+ return self._text_blocks
101
+
102
+ def _sender_loop(self):
103
+ """Background thread: batch events and POST to API."""
104
+ buffer: list[dict] = []
105
+ last_flush = time.monotonic()
106
+
107
+ while not self._done.is_set() or not self._queue.empty():
108
+ # Drain queue
109
+ try:
110
+ item = self._queue.get(timeout=0.25)
111
+ buffer.append(item)
112
+ except queue.Empty:
113
+ pass
114
+
115
+ # Flush conditions
116
+ now = time.monotonic()
117
+ should_flush = (
118
+ len(buffer) >= self.BATCH_SIZE
119
+ or (buffer and now - last_flush >= self.FLUSH_INTERVAL_S)
120
+ or (buffer and self._done.is_set())
121
+ )
122
+
123
+ if should_flush:
124
+ self._flush(buffer)
125
+ buffer = []
126
+ last_flush = now
127
+
128
+ def _flush(self, events: list[dict]):
129
+ """POST batch to API. Swallow errors — log file is the safety net."""
130
+ if not events:
131
+ return
132
+ try:
133
+ api_post(
134
+ f"/api/agent-sessions/{self._session_id}/relay-batch",
135
+ json={"events": events},
136
+ )
137
+ except Exception:
138
+ pass # Data preserved in log file
139
+
140
+
141
+ def api_patch(path: str, json: dict) -> dict:
142
+ """PATCH request helper (not in api_client.py yet)."""
143
+ from .api_client import get_client
144
+
145
+ with get_client() as client:
146
+ resp = client.patch(path, json=json)
147
+ resp.raise_for_status()
148
+ return resp.json()
149
+
150
+
151
+ def update_phase(session_id: int | str, phase: str, **kwargs):
152
+ """Update agent session phase via API."""
153
+ body: dict = {"phase": phase}
154
+ body.update(kwargs)
155
+ try:
156
+ api_patch(f"/api/agent-sessions/{session_id}/phase", json=body)
157
+ except Exception:
158
+ pass
159
+
160
+
161
+ def complete_session(session_id: int | str, status: str, **kwargs):
162
+ """Mark agent session as completed/failed via API."""
163
+ body: dict = {"status": status}
164
+ body.update(kwargs)
165
+ try:
166
+ api_post(f"/api/agent-sessions/{session_id}/complete", json=body)
167
+ except Exception:
168
+ pass
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "bumblebee-cli"
3
- version = "0.1.0"
3
+ version = "0.2.0"
4
4
  description = "Bumblebee CLI - bb command for dev task management"
5
5
  requires-python = ">=3.12"
6
6
  dependencies = [
@@ -1,4 +1,4 @@
1
- typer>=0.15.0
2
- httpx>=0.28.0
3
- rich>=13.9.0
4
- toml>=0.10.2
1
+ typer>=0.15.0
2
+ httpx>=0.28.0
3
+ rich>=13.9.0
4
+ toml>=0.10.2
package/scripts/build.sh CHANGED
@@ -1,20 +1,20 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- # Build script: copies the Python CLI source into the npm package for publishing.
5
- # Run from repo root: bash packages/npm-cli/scripts/build.sh
6
-
7
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
- PKG_DIR="$(dirname "$SCRIPT_DIR")"
9
- REPO_ROOT="$(dirname "$(dirname "$PKG_DIR")")"
10
-
11
- DEST="$PKG_DIR/python"
12
-
13
- echo "Copying CLI source to $DEST..."
14
- rm -rf "$DEST"
15
- cp -r "$REPO_ROOT/cli" "$DEST"
16
-
17
- # Clean up build artifacts
18
- rm -rf "$DEST"/__pycache__ "$DEST"/**/__pycache__ "$DEST"/*.egg-info "$DEST"/dist "$DEST"/build
19
-
20
- echo "Build complete. Ready to publish with: cd $PKG_DIR && npm publish"
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # Build script: copies the Python CLI source into the npm package for publishing.
5
+ # Run from repo root: bash packages/npm-cli/scripts/build.sh
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ PKG_DIR="$(dirname "$SCRIPT_DIR")"
9
+ REPO_ROOT="$(dirname "$(dirname "$PKG_DIR")")"
10
+
11
+ DEST="$PKG_DIR/python"
12
+
13
+ echo "Copying CLI source to $DEST..."
14
+ rm -rf "$DEST"
15
+ cp -r "$REPO_ROOT/cli" "$DEST"
16
+
17
+ # Clean up build artifacts
18
+ rm -rf "$DEST"/__pycache__ "$DEST"/**/__pycache__ "$DEST"/*.egg-info "$DEST"/dist "$DEST"/build
19
+
20
+ echo "Build complete. Ready to publish with: cd $PKG_DIR && npm publish"
@@ -1,121 +1,146 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Postinstall script for bumblebee-cli
5
- *
6
- * - Checks for Python 3.12+
7
- * - Skips if `bb` is already on PATH (user installed via pip)
8
- * - Creates a local .venv and installs the Python CLI
9
- * - NEVER fails npm install — warns instead
10
- */
11
-
12
- import { execSync } from "node:child_process";
13
- import { existsSync } from "node:fs";
14
- import { dirname, join } from "node:path";
15
- import { fileURLToPath } from "node:url";
16
-
17
- const __dirname = dirname(fileURLToPath(import.meta.url));
18
- const pkgRoot = join(__dirname, "..");
19
- const pythonDir = join(pkgRoot, "python");
20
-
21
- function log(msg) {
22
- console.log(`[bumblebee-cli] ${msg}`);
23
- }
24
-
25
- function warn(msg) {
26
- console.warn(`[bumblebee-cli] WARNING: ${msg}`);
27
- }
28
-
29
- // Check if bb is already available
30
- function isBbInstalled() {
31
- try {
32
- const which = process.platform === "win32" ? "where" : "which";
33
- execSync(`${which} bb`, { stdio: "pipe" });
34
- return true;
35
- } catch {
36
- return false;
37
- }
38
- }
39
-
40
- // Find python and check version
41
- function findPython() {
42
- const candidates = ["python3", "python"];
43
- for (const cmd of candidates) {
44
- try {
45
- const version = execSync(`${cmd} --version`, {
46
- encoding: "utf8",
47
- stdio: ["pipe", "pipe", "pipe"],
48
- }).trim();
49
- // Parse "Python 3.12.x"
50
- const match = version.match(/Python (\d+)\.(\d+)/);
51
- if (match) {
52
- const major = parseInt(match[1], 10);
53
- const minor = parseInt(match[2], 10);
54
- if (major === 3 && minor >= 12) {
55
- return { cmd, version };
56
- }
57
- }
58
- } catch {
59
- // try next
60
- }
61
- }
62
- return null;
63
- }
64
-
65
- function main() {
66
- // Skip if bb already installed via pip
67
- if (isBbInstalled()) {
68
- log("'bb' command found on PATH — skipping Python setup.");
69
- return;
70
- }
71
-
72
- // Find suitable Python
73
- const python = findPython();
74
- if (!python) {
75
- warn(
76
- "Python 3.12+ not found. The 'bb' command won't work until you:\n" +
77
- " 1. Install Python 3.12+ (https://python.org)\n" +
78
- " 2. Run: pip install bumblebee-cli"
79
- );
80
- return;
81
- }
82
-
83
- log(`Found ${python.version} (${python.cmd})`);
84
-
85
- // Create .venv and install
86
- const venvDir = join(pkgRoot, ".venv");
87
- const isWin = process.platform === "win32";
88
- const pip = isWin
89
- ? join(venvDir, "Scripts", "pip.exe")
90
- : join(venvDir, "bin", "pip");
91
-
92
- try {
93
- if (!existsSync(venvDir)) {
94
- log("Creating virtual environment...");
95
- execSync(`${python.cmd} -m venv "${venvDir}"`, { stdio: "pipe" });
96
- }
97
-
98
- // Install from bundled python/ dir if available, otherwise from PyPI
99
- if (existsSync(pythonDir)) {
100
- log("Installing Bumblebee CLI from bundled source...");
101
- execSync(`"${pip}" install "${pythonDir}"`, { stdio: "pipe" });
102
- } else {
103
- log("Installing Bumblebee CLI from PyPI...");
104
- execSync(`"${pip}" install bumblebee-cli`, { stdio: "pipe" });
105
- }
106
-
107
- log("Setup complete! Run 'bb --help' to get started.");
108
- } catch (e) {
109
- warn(
110
- `Python setup failed: ${e.message}\n` +
111
- "You can still install manually: pip install bumblebee-cli"
112
- );
113
- }
114
- }
115
-
116
- try {
117
- main();
118
- } catch {
119
- // Never fail npm install
120
- warn("Postinstall encountered an error, but npm install will continue.");
121
- }
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Postinstall script for bumblebee-cli
5
+ *
6
+ * - Checks for Python 3.12+
7
+ * - Skips if a real (pip-installed) `bb` is already on PATH
8
+ * - Creates a local .venv and installs the Python CLI
9
+ * - NEVER fails npm install — warns instead
10
+ */
11
+
12
+ import { execSync } from "node:child_process";
13
+ import { existsSync } from "node:fs";
14
+ import { dirname, join, resolve } from "node:path";
15
+ import { fileURLToPath } from "node:url";
16
+
17
+ const __dirname = dirname(fileURLToPath(import.meta.url));
18
+ const pkgRoot = join(__dirname, "..");
19
+ const pythonDir = join(pkgRoot, "python");
20
+
21
+ function log(msg) {
22
+ console.log(`[bumblebee-cli] ${msg}`);
23
+ }
24
+
25
+ function warn(msg) {
26
+ console.warn(`[bumblebee-cli] WARNING: ${msg}`);
27
+ }
28
+
29
+ // Check if a real (non-npm-wrapper) bb is already available
30
+ function isPipBbInstalled() {
31
+ try {
32
+ // Try running bb --help and check if it's the Python CLI (not our wrapper)
33
+ const which = process.platform === "win32" ? "where" : "which";
34
+ const result = execSync(`${which} bb`, { encoding: "utf8", stdio: "pipe" });
35
+ const paths = result.trim().split(/\r?\n/);
36
+
37
+ // Get npm global prefix to filter out our own wrapper
38
+ let npmPrefix = null;
39
+ try {
40
+ npmPrefix = execSync("npm prefix -g", { encoding: "utf8", stdio: "pipe" }).trim();
41
+ } catch {}
42
+
43
+ for (const bbPath of paths) {
44
+ const p = bbPath.trim();
45
+ if (!p) continue;
46
+ const normalized = resolve(p).toLowerCase();
47
+ // Skip if it's inside npm global prefix (that's our own wrapper)
48
+ if (npmPrefix && normalized.startsWith(resolve(npmPrefix).toLowerCase())) {
49
+ continue;
50
+ }
51
+ // Skip if it's inside node_modules
52
+ if (normalized.includes("node_modules") && normalized.includes("bumblebee-cli")) {
53
+ continue;
54
+ }
55
+ // Found a real bb (pip-installed)
56
+ return true;
57
+ }
58
+ } catch {
59
+ // not found
60
+ }
61
+ return false;
62
+ }
63
+
64
+ // Find python and check version
65
+ function findPython() {
66
+ const candidates =
67
+ process.platform === "win32" ? ["python", "python3"] : ["python3", "python"];
68
+ for (const cmd of candidates) {
69
+ try {
70
+ const version = execSync(`${cmd} --version`, {
71
+ encoding: "utf8",
72
+ stdio: ["pipe", "pipe", "pipe"],
73
+ }).trim();
74
+ // Parse "Python 3.12.x"
75
+ const match = version.match(/Python (\d+)\.(\d+)/);
76
+ if (match) {
77
+ const major = parseInt(match[1], 10);
78
+ const minor = parseInt(match[2], 10);
79
+ if (major === 3 && minor >= 12) {
80
+ return { cmd, version };
81
+ }
82
+ }
83
+ } catch {
84
+ // try next
85
+ }
86
+ }
87
+ return null;
88
+ }
89
+
90
+ function main() {
91
+ // Skip if bb already installed via pip
92
+ if (isPipBbInstalled()) {
93
+ log("'bb' command found on PATH (pip) — skipping Python setup.");
94
+ return;
95
+ }
96
+
97
+ // Find suitable Python
98
+ const python = findPython();
99
+ if (!python) {
100
+ warn(
101
+ "Python 3.12+ not found. The 'bb' command won't work until you:\n" +
102
+ " 1. Install Python 3.12+ (https://python.org)\n" +
103
+ " 2. Run: pip install bumblebee-cli"
104
+ );
105
+ return;
106
+ }
107
+
108
+ log(`Found ${python.version} (${python.cmd})`);
109
+
110
+ // Create .venv and install
111
+ const venvDir = join(pkgRoot, ".venv");
112
+ const isWin = process.platform === "win32";
113
+ const pip = isWin
114
+ ? join(venvDir, "Scripts", "pip.exe")
115
+ : join(venvDir, "bin", "pip");
116
+
117
+ try {
118
+ if (!existsSync(venvDir)) {
119
+ log("Creating virtual environment...");
120
+ execSync(`${python.cmd} -m venv "${venvDir}"`, { stdio: "pipe" });
121
+ }
122
+
123
+ // Install from bundled python/ dir if available, otherwise from PyPI
124
+ if (existsSync(pythonDir)) {
125
+ log("Installing Bumblebee CLI from bundled source...");
126
+ execSync(`"${pip}" install "${pythonDir}"`, { stdio: "pipe" });
127
+ } else {
128
+ log("Installing Bumblebee CLI from PyPI...");
129
+ execSync(`"${pip}" install bumblebee-cli`, { stdio: "pipe" });
130
+ }
131
+
132
+ log("Setup complete! Run 'bb --help' to get started.");
133
+ } catch (e) {
134
+ warn(
135
+ `Python setup failed: ${e.message}\n` +
136
+ "You can still install manually: pip install bumblebee-cli"
137
+ );
138
+ }
139
+ }
140
+
141
+ try {
142
+ main();
143
+ } catch {
144
+ // Never fail npm install
145
+ warn("Postinstall encountered an error, but npm install will continue.");
146
+ }
@@ -1,9 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: bumblebee-cli
3
- Version: 0.1.0
4
- Summary: Bumblebee CLI — bb command for dev task management
5
- Requires-Python: >=3.12
6
- Requires-Dist: typer>=0.15.0
7
- Requires-Dist: httpx>=0.28.0
8
- Requires-Dist: rich>=13.9.0
9
- Requires-Dist: toml>=0.10.2
@@ -1,21 +0,0 @@
1
- pyproject.toml
2
- src/__init__.py
3
- src/api_client.py
4
- src/config.py
5
- src/main.py
6
- src/bumblebee_cli.egg-info/PKG-INFO
7
- src/bumblebee_cli.egg-info/SOURCES.txt
8
- src/bumblebee_cli.egg-info/dependency_links.txt
9
- src/bumblebee_cli.egg-info/entry_points.txt
10
- src/bumblebee_cli.egg-info/requires.txt
11
- src/bumblebee_cli.egg-info/top_level.txt
12
- src/commands/__init__.py
13
- src/commands/agent.py
14
- src/commands/auth.py
15
- src/commands/board.py
16
- src/commands/comment.py
17
- src/commands/label.py
18
- src/commands/project.py
19
- src/commands/sprint.py
20
- src/commands/story.py
21
- src/commands/task.py
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- bb = src.main:app
@@ -1,5 +0,0 @@
1
- __init__
2
- api_client
3
- commands
4
- config
5
- main