bumblebee-cli 0.2.0 → 0.3.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/dist/index.js +4214 -0
- package/dist/index.js.map +1 -0
- package/package.json +32 -18
- package/templates/skills/bb-agent/SKILL.md +180 -0
- package/templates/skills/bb-agent/references/bb-commands.md +124 -0
- package/templates/skills/bb-agent/references/investigate-workflow.md +150 -0
- package/templates/skills/bb-agent/references/parallel-workflow.md +105 -0
- package/templates/skills/bb-agent/references/prompts.md +144 -0
- package/templates/skills/bb-agent/references/status-transitions.md +93 -0
- package/templates/skills/bb-agent/references/workflow.md +178 -0
- package/README.md +0 -49
- package/bin/bb.mjs +0 -132
- package/python/bb_cli/__init__.py +0 -0
- package/python/bb_cli/__main__.py +0 -3
- package/python/bb_cli/api_client.py +0 -45
- package/python/bb_cli/commands/__init__.py +0 -0
- package/python/bb_cli/commands/agent.py +0 -2287
- package/python/bb_cli/commands/auth.py +0 -79
- package/python/bb_cli/commands/board.py +0 -47
- package/python/bb_cli/commands/comment.py +0 -34
- package/python/bb_cli/commands/daemon.py +0 -153
- package/python/bb_cli/commands/init.py +0 -83
- package/python/bb_cli/commands/item.py +0 -192
- package/python/bb_cli/commands/label.py +0 -43
- package/python/bb_cli/commands/project.py +0 -175
- package/python/bb_cli/commands/sprint.py +0 -84
- package/python/bb_cli/config.py +0 -136
- package/python/bb_cli/main.py +0 -44
- package/python/bb_cli/progress.py +0 -117
- package/python/bb_cli/streaming.py +0 -168
- package/python/pyproject.toml +0 -18
- package/python/requirements.txt +0 -4
- package/scripts/build.sh +0 -20
- package/scripts/postinstall.mjs +0 -146
|
@@ -1,168 +0,0 @@
|
|
|
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
|
package/python/pyproject.toml
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "bumblebee-cli"
|
|
3
|
-
version = "0.2.0"
|
|
4
|
-
description = "Bumblebee CLI - bb command for dev task management"
|
|
5
|
-
requires-python = ">=3.12"
|
|
6
|
-
dependencies = [
|
|
7
|
-
"typer>=0.15.0",
|
|
8
|
-
"httpx>=0.28.0",
|
|
9
|
-
"rich>=13.9.0",
|
|
10
|
-
"toml>=0.10.2",
|
|
11
|
-
]
|
|
12
|
-
|
|
13
|
-
[project.scripts]
|
|
14
|
-
bb = "bb_cli.main:app"
|
|
15
|
-
|
|
16
|
-
[build-system]
|
|
17
|
-
requires = ["setuptools>=75.0"]
|
|
18
|
-
build-backend = "setuptools.build_meta"
|
package/python/requirements.txt
DELETED
package/scripts/build.sh
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
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"
|
package/scripts/postinstall.mjs
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
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
|
-
}
|