bumblebee-cli 0.1.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/README.md +47 -0
- package/bin/bb.mjs +102 -0
- package/package.json +28 -0
- package/python/bb_cli/__init__.py +0 -0
- package/python/bb_cli/api_client.py +38 -0
- package/python/bb_cli/bumblebee_cli.egg-info/PKG-INFO +9 -0
- package/python/bb_cli/bumblebee_cli.egg-info/SOURCES.txt +21 -0
- package/python/bb_cli/bumblebee_cli.egg-info/dependency_links.txt +1 -0
- package/python/bb_cli/bumblebee_cli.egg-info/entry_points.txt +2 -0
- package/python/bb_cli/bumblebee_cli.egg-info/requires.txt +4 -0
- package/python/bb_cli/bumblebee_cli.egg-info/top_level.txt +5 -0
- package/python/bb_cli/commands/__init__.py +0 -0
- package/python/bb_cli/commands/__pycache__/__init__.cpython-313.pyc +0 -0
- package/python/bb_cli/commands/__pycache__/agent.cpython-313.pyc +0 -0
- package/python/bb_cli/commands/__pycache__/auth.cpython-313.pyc +0 -0
- package/python/bb_cli/commands/__pycache__/board.cpython-313.pyc +0 -0
- package/python/bb_cli/commands/__pycache__/comment.cpython-313.pyc +0 -0
- package/python/bb_cli/commands/__pycache__/init.cpython-313.pyc +0 -0
- package/python/bb_cli/commands/__pycache__/item.cpython-313.pyc +0 -0
- package/python/bb_cli/commands/__pycache__/label.cpython-313.pyc +0 -0
- package/python/bb_cli/commands/__pycache__/project.cpython-313.pyc +0 -0
- package/python/bb_cli/commands/__pycache__/sprint.cpython-313.pyc +0 -0
- package/python/bb_cli/commands/__pycache__/story.cpython-313.pyc +0 -0
- package/python/bb_cli/commands/__pycache__/task.cpython-313.pyc +0 -0
- package/python/bb_cli/commands/agent.py +1030 -0
- package/python/bb_cli/commands/auth.py +79 -0
- package/python/bb_cli/commands/board.py +47 -0
- package/python/bb_cli/commands/comment.py +34 -0
- package/python/bb_cli/commands/init.py +62 -0
- package/python/bb_cli/commands/item.py +192 -0
- package/python/bb_cli/commands/label.py +43 -0
- package/python/bb_cli/commands/project.py +111 -0
- package/python/bb_cli/commands/sprint.py +84 -0
- package/python/bb_cli/config.py +136 -0
- package/python/bb_cli/main.py +44 -0
- package/python/pyproject.toml +18 -0
- package/scripts/build.sh +20 -0
- package/scripts/postinstall.mjs +121 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import toml
|
|
5
|
+
|
|
6
|
+
# Global config (always ~/.bumblebee/)
|
|
7
|
+
GLOBAL_CONFIG_DIR = Path.home() / ".bumblebee"
|
|
8
|
+
GLOBAL_CONFIG_FILE = GLOBAL_CONFIG_DIR / "config.toml"
|
|
9
|
+
|
|
10
|
+
# Backward compat alias (used by agent.py for worktrees)
|
|
11
|
+
CONFIG_DIR = GLOBAL_CONFIG_DIR
|
|
12
|
+
|
|
13
|
+
# Local config directory name
|
|
14
|
+
LOCAL_DIR_NAME = ".bumblebee"
|
|
15
|
+
LOCAL_CONFIG_NAME = "config.toml"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def ensure_config_dir():
|
|
19
|
+
GLOBAL_CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def find_project_root(start: Path | None = None) -> Path | None:
|
|
23
|
+
"""Walk up from start (default cwd) looking for .bumblebee/ or .git/."""
|
|
24
|
+
current = (start or Path.cwd()).resolve()
|
|
25
|
+
for directory in [current, *current.parents]:
|
|
26
|
+
if (directory / LOCAL_DIR_NAME).is_dir():
|
|
27
|
+
return directory
|
|
28
|
+
if (directory / ".git").exists():
|
|
29
|
+
return directory
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_local_config_dir() -> Path | None:
|
|
34
|
+
"""Return .bumblebee/ directory if it exists in the project root."""
|
|
35
|
+
root = find_project_root()
|
|
36
|
+
if root and (root / LOCAL_DIR_NAME).is_dir():
|
|
37
|
+
return root / LOCAL_DIR_NAME
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def is_local_config_active() -> bool:
|
|
42
|
+
"""Check if a local .bumblebee/ config exists."""
|
|
43
|
+
return get_local_config_dir() is not None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _load_global_config() -> dict:
|
|
47
|
+
if GLOBAL_CONFIG_FILE.exists():
|
|
48
|
+
return toml.load(GLOBAL_CONFIG_FILE)
|
|
49
|
+
return {}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def load_local_config() -> dict:
|
|
53
|
+
"""Load config from project-local .bumblebee/config.toml."""
|
|
54
|
+
local_dir = get_local_config_dir()
|
|
55
|
+
if local_dir:
|
|
56
|
+
local_file = local_dir / LOCAL_CONFIG_NAME
|
|
57
|
+
if local_file.exists():
|
|
58
|
+
return toml.load(local_file)
|
|
59
|
+
return {}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def save_local_config(data: dict):
|
|
63
|
+
"""Write config to project-local .bumblebee/config.toml."""
|
|
64
|
+
local_dir = get_local_config_dir()
|
|
65
|
+
if not local_dir:
|
|
66
|
+
raise FileNotFoundError(
|
|
67
|
+
"No .bumblebee/ directory found. Run 'bb init' first."
|
|
68
|
+
)
|
|
69
|
+
local_dir.mkdir(parents=True, exist_ok=True)
|
|
70
|
+
with open(local_dir / LOCAL_CONFIG_NAME, "w") as f:
|
|
71
|
+
toml.dump(data, f)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def load_config() -> dict:
|
|
75
|
+
"""Load merged config: local .bumblebee/config.toml over global ~/.bumblebee/config.toml.
|
|
76
|
+
|
|
77
|
+
Env vars (BB_API_URL etc.) are handled in individual getters, not here.
|
|
78
|
+
Credentials (token) are never read from local config.
|
|
79
|
+
"""
|
|
80
|
+
global_cfg = _load_global_config()
|
|
81
|
+
local_cfg = load_local_config()
|
|
82
|
+
|
|
83
|
+
# Shallow merge: local overrides global for top-level keys
|
|
84
|
+
# But never pull token from local config
|
|
85
|
+
local_cfg.pop("token", None)
|
|
86
|
+
|
|
87
|
+
merged = {**global_cfg, **local_cfg}
|
|
88
|
+
return merged
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def save_config(data: dict):
|
|
92
|
+
"""Save config to global ~/.bumblebee/config.toml (backward compatible)."""
|
|
93
|
+
ensure_config_dir()
|
|
94
|
+
with open(GLOBAL_CONFIG_FILE, "w") as f:
|
|
95
|
+
toml.dump(data, f)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def get_api_url() -> str:
|
|
99
|
+
# Env var takes highest priority
|
|
100
|
+
env_url = os.environ.get("BB_API_URL")
|
|
101
|
+
if env_url:
|
|
102
|
+
return env_url
|
|
103
|
+
cfg = load_config()
|
|
104
|
+
return cfg.get("api_url", "http://localhost:8000")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def get_token() -> str | None:
|
|
108
|
+
"""Get auth token — always from global config only."""
|
|
109
|
+
cfg = _load_global_config()
|
|
110
|
+
return cfg.get("token")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def get_current_project() -> str | None:
|
|
114
|
+
cfg = load_config()
|
|
115
|
+
return cfg.get("current_project")
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def get_project_path(slug: str | None = None) -> str | None:
|
|
119
|
+
"""Get the local source code path for a project."""
|
|
120
|
+
cfg = load_config()
|
|
121
|
+
target = slug or cfg.get("current_project")
|
|
122
|
+
if not target:
|
|
123
|
+
return None
|
|
124
|
+
projects = cfg.get("projects", {})
|
|
125
|
+
return projects.get(target, {}).get("path")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def set_project_path(slug: str, path: str):
|
|
129
|
+
"""Set the local source code path for a project."""
|
|
130
|
+
cfg = _load_global_config()
|
|
131
|
+
if "projects" not in cfg:
|
|
132
|
+
cfg["projects"] = {}
|
|
133
|
+
if slug not in cfg["projects"]:
|
|
134
|
+
cfg["projects"][slug] = {}
|
|
135
|
+
cfg["projects"][slug]["path"] = path
|
|
136
|
+
save_config(cfg)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
|
|
3
|
+
from .commands import agent, auth, board, comment, init, item, label, project, sprint
|
|
4
|
+
|
|
5
|
+
app = typer.Typer(
|
|
6
|
+
name="bb",
|
|
7
|
+
help="Bumblebee - Dev Task Management + Claude Code Automation",
|
|
8
|
+
no_args_is_help=True,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
# Register subcommands
|
|
12
|
+
app.add_typer(auth.app, name="auth", help="Login, register, logout")
|
|
13
|
+
app.add_typer(project.app, name="project")
|
|
14
|
+
app.add_typer(item.app, name="item")
|
|
15
|
+
app.add_typer(sprint.app, name="sprint")
|
|
16
|
+
app.add_typer(comment.app, name="comment")
|
|
17
|
+
app.add_typer(label.app, name="label")
|
|
18
|
+
app.add_typer(board.app, name="board")
|
|
19
|
+
app.add_typer(agent.app, name="agent")
|
|
20
|
+
|
|
21
|
+
# Top-level commands
|
|
22
|
+
app.command(name="init")(init.init)
|
|
23
|
+
|
|
24
|
+
# Convenience aliases at top level
|
|
25
|
+
login_app = typer.Typer()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@app.command(name="login")
|
|
29
|
+
def login(
|
|
30
|
+
email: str = typer.Option(..., prompt=True),
|
|
31
|
+
password: str = typer.Option(..., prompt=True, hide_input=True),
|
|
32
|
+
):
|
|
33
|
+
"""Shortcut for bb auth login."""
|
|
34
|
+
auth.login(email=email, password=password)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@app.command(name="logout")
|
|
38
|
+
def logout():
|
|
39
|
+
"""Shortcut for bb auth logout."""
|
|
40
|
+
auth.logout()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if __name__ == "__main__":
|
|
44
|
+
app()
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "bumblebee-cli"
|
|
3
|
+
version = "0.1.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/scripts/build.sh
ADDED
|
@@ -0,0 +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"
|
|
@@ -0,0 +1,121 @@
|
|
|
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
|
+
}
|