bob-dev 0.2.2__tar.gz → 0.2.3__tar.gz
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.
- {bob_dev-0.2.2/src/bob_dev.egg-info → bob_dev-0.2.3}/PKG-INFO +1 -1
- {bob_dev-0.2.2 → bob_dev-0.2.3}/pyproject.toml +1 -1
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/bob_dev/cli.py +11 -15
- bob_dev-0.2.3/src/bob_dev/services/claude.py +54 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/bob_dev/services/terminal.py +1 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3/src/bob_dev.egg-info}/PKG-INFO +1 -1
- bob_dev-0.2.2/src/bob_dev/services/claude.py +0 -27
- {bob_dev-0.2.2 → bob_dev-0.2.3}/LICENSE +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/README.md +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/setup.cfg +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/__init__.py +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/bob_dev/__init__.py +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/bob_dev/constants/__init__.py +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/bob_dev/constants/frameworks.py +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/bob_dev/services/__init__.py +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/bob_dev/services/config.py +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/bob_dev/services/gitlab.py +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/bob_dev/services/jira.py +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/bob_dev/services/llm.py +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/bob_dev/services/project.py +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/bob_dev.egg-info/SOURCES.txt +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/bob_dev.egg-info/dependency_links.txt +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/bob_dev.egg-info/entry_points.txt +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/bob_dev.egg-info/requires.txt +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/src/bob_dev.egg-info/top_level.txt +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/tests/test_cli.py +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/tests/test_config.py +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/tests/test_jira.py +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/tests/test_llm.py +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/tests/test_project.py +0 -0
- {bob_dev-0.2.2 → bob_dev-0.2.3}/tests/test_terminal.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "bob-dev"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.3"
|
|
8
8
|
description = "An AI developer to write code for you"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name="Samuel Pereira dos Santos", email="samuelsantosdev@gmail.com" }]
|
|
@@ -33,6 +33,7 @@ import sys
|
|
|
33
33
|
import argparse
|
|
34
34
|
import asyncio
|
|
35
35
|
from pathlib import Path
|
|
36
|
+
from time import sleep, time
|
|
36
37
|
|
|
37
38
|
from InquirerPy import inquirer
|
|
38
39
|
from dotenv import load_dotenv
|
|
@@ -60,8 +61,8 @@ from .services.config import check_configuration, update_env_file
|
|
|
60
61
|
# ---------------------------------------------------------------------------
|
|
61
62
|
|
|
62
63
|
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
63
|
-
load_dotenv(SCRIPT_DIR / ".env")
|
|
64
64
|
ENV_PATH = Path.home() / ".bob_dev" / ".env"
|
|
65
|
+
load_dotenv(ENV_PATH) # Load .env if it exists, but don't require it
|
|
65
66
|
|
|
66
67
|
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "")
|
|
67
68
|
GROK_API_KEY = os.environ.get("GROK_API_KEY", "")
|
|
@@ -221,39 +222,34 @@ def main() -> None:
|
|
|
221
222
|
))
|
|
222
223
|
|
|
223
224
|
print(f"\n{BOLD}── Prompt Analysis {'─' * 50}{RESET}")
|
|
224
|
-
|
|
225
|
+
for line in analysis.splitlines():
|
|
226
|
+
print(line)
|
|
227
|
+
sleep(0.02) # Simulate a "typing" effect for better readability
|
|
225
228
|
print("─" * 68 + "\n")
|
|
226
229
|
|
|
227
230
|
# ── Confirm before handing off to Claude Code ────────────────────────────
|
|
228
|
-
answer = input("Proceed
|
|
231
|
+
answer = input("Proceed to prompt preview? [y/N] ").strip().lower()
|
|
229
232
|
if answer != "y":
|
|
230
233
|
prompt_file = SCRIPT_DIR / f"claude_prompt-{task_id}.md"
|
|
231
234
|
prompt_file.write_text(prompt_md, encoding="utf-8")
|
|
232
235
|
print_info(f"Aborted. Prompt saved to {prompt_file}")
|
|
233
236
|
sys.exit(0)
|
|
234
237
|
|
|
235
|
-
print_info("
|
|
238
|
+
print_info("\n\n\nPrompt preview:")
|
|
236
239
|
print("-" * 68)
|
|
237
|
-
|
|
240
|
+
for line in prompt_md.splitlines():
|
|
241
|
+
print(line)
|
|
242
|
+
sleep(0.02) # Simulate a "typing" effect for better readability
|
|
238
243
|
print("-" * 68)
|
|
239
244
|
answer = input("\nAre you sure? This will run the Claude Code CLI with the generated prompt. [y/N] ").strip().lower()
|
|
240
245
|
if answer != "y":
|
|
241
246
|
print_info("Aborted by user.")
|
|
242
247
|
sys.exit(0)
|
|
243
|
-
|
|
244
|
-
answer = input("Do you want select a agent to do this development? [y/N] ").strip().upper()
|
|
245
|
-
agent_claude = ""
|
|
246
|
-
if answer == "Y":
|
|
247
|
-
agents_of_claude = read_agents_from_claude(CLAUDE_CODE_CMD)
|
|
248
|
-
agent_claude = inquirer.select(
|
|
249
|
-
message="Select the agent to do the development:",
|
|
250
|
-
choices=agents_of_claude,
|
|
251
|
-
).execute()
|
|
252
248
|
|
|
253
249
|
# ── Step 4 – Pass prompt to Claude Code ──────────────────────────────────
|
|
254
250
|
print_step("[4/4]", "Passing prompt to Claude Code …")
|
|
255
251
|
print()
|
|
256
|
-
asyncio.run(_pass_to_claude_code(prompt_md, task_id,
|
|
252
|
+
asyncio.run(_pass_to_claude_code(prompt_md, task_id, None))
|
|
257
253
|
|
|
258
254
|
|
|
259
255
|
# ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""claude.py
|
|
2
|
+
|
|
3
|
+
Claude Code utilities for bob_dev:
|
|
4
|
+
- List available agents from the Claude Code CLI.
|
|
5
|
+
- Run Claude Code commands and handle errors.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import subprocess
|
|
9
|
+
import json
|
|
10
|
+
|
|
11
|
+
from ..services.terminal import print_error
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def read_agents_from_claude(claude_cmd: str) -> list[str]:
|
|
15
|
+
"""Run *claude_cmd* with --list-agents and parse the output into a list."""
|
|
16
|
+
try:
|
|
17
|
+
# list the installed agents by running the CLI command. This is more reliable than
|
|
18
|
+
# trying to read the plugin directory directly, which may have different permissions or structure.
|
|
19
|
+
# The expected output is a list of agent names, one per line. We strip whitespace and ignore empty lines.
|
|
20
|
+
|
|
21
|
+
result_marketplace = subprocess.run(
|
|
22
|
+
["ls", "~/.claude/plugins/marketplaces/claude-plugins-official/plugins"],
|
|
23
|
+
stdout=subprocess.PIPE,
|
|
24
|
+
stderr=subprocess.PIPE,
|
|
25
|
+
text=True,
|
|
26
|
+
check=True,
|
|
27
|
+
)
|
|
28
|
+
result_agents = subprocess.run(
|
|
29
|
+
[claude_cmd, "agents", "--json"],
|
|
30
|
+
stdout=subprocess.PIPE,
|
|
31
|
+
stderr=subprocess.PIPE,
|
|
32
|
+
text=True,
|
|
33
|
+
check=True,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
agents_data = json.loads(result_agents.stdout)
|
|
37
|
+
# We want to extract the "name" field from each agent,
|
|
38
|
+
# but only if it has one (background agents may not have a name).
|
|
39
|
+
# We also want to include the marketplace plugins as agents,
|
|
40
|
+
# since they can be invoked by name as well.
|
|
41
|
+
# The marketplace plugins are just the filenames in the marketplace directory.
|
|
42
|
+
result_agents = [str(agent.get("name")).strip() for agent in agents_data if agent.get("name")]
|
|
43
|
+
agents_marketplace = [line.strip() for line in result_marketplace.stdout.splitlines() if line.strip()]
|
|
44
|
+
# Combine agents from both sources, ensuring uniqueness while preserving order.
|
|
45
|
+
seen = set()
|
|
46
|
+
combined_agents = []
|
|
47
|
+
for agent in result_agents + agents_marketplace:
|
|
48
|
+
if agent not in seen:
|
|
49
|
+
seen.add(agent)
|
|
50
|
+
combined_agents.append(agent)
|
|
51
|
+
return combined_agents
|
|
52
|
+
except Exception as exc:
|
|
53
|
+
print_error(f"Failed to list agents from Claude Code CLI: {exc}")
|
|
54
|
+
return []
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"""claude.py
|
|
2
|
-
|
|
3
|
-
Claude Code utilities for bob_dev:
|
|
4
|
-
- List available agents from the Claude Code CLI.
|
|
5
|
-
- Run Claude Code commands and handle errors.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import subprocess
|
|
9
|
-
|
|
10
|
-
from ..services.terminal import print_error
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def read_agents_from_claude(claude_cmd: str) -> list[str]:
|
|
14
|
-
"""Run *claude_cmd* with --list-agents and parse the output into a list."""
|
|
15
|
-
try:
|
|
16
|
-
result = subprocess.run(
|
|
17
|
-
[claude_cmd, "--list-agents"],
|
|
18
|
-
stdout=subprocess.PIPE,
|
|
19
|
-
stderr=subprocess.PIPE,
|
|
20
|
-
text=True,
|
|
21
|
-
check=True,
|
|
22
|
-
)
|
|
23
|
-
agents = [line.strip() for line in result.stdout.splitlines() if line.strip()]
|
|
24
|
-
return agents
|
|
25
|
-
except Exception as exc:
|
|
26
|
-
print_error(f"Failed to list agents from Claude Code CLI: {exc}")
|
|
27
|
-
return []
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|