onecoder 0.0.2__py3-none-any.whl
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.
- onecoder/agent.py +95 -0
- onecoder/agentic_tool_search/__init__.py +0 -0
- onecoder/agentic_tool_search/dynamic_tool_search.py +64 -0
- onecoder/agentic_tool_search/registry.py +33 -0
- onecoder/agents/__init__.py +7 -0
- onecoder/agents/documentation_agent.py +12 -0
- onecoder/agents/file_reader_agent.py +19 -0
- onecoder/agents/file_writer_agent.py +19 -0
- onecoder/agents/orchestrator_agent.py +51 -0
- onecoder/agents/refactoring_agent.py +12 -0
- onecoder/agents/research_agent.py +31 -0
- onecoder/agents/task_suggestion_agent.py +88 -0
- onecoder/alignment.py +236 -0
- onecoder/api.py +162 -0
- onecoder/api_client.py +112 -0
- onecoder/backends/base.py +22 -0
- onecoder/backends/local_tui.py +65 -0
- onecoder/blackboard.py +102 -0
- onecoder/cli.py +108 -0
- onecoder/commands/__init__.py +1 -0
- onecoder/commands/auth.py +78 -0
- onecoder/commands/ci.py +29 -0
- onecoder/commands/delegate.py +557 -0
- onecoder/commands/doctor.py +40 -0
- onecoder/commands/issue.py +136 -0
- onecoder/commands/logs.py +45 -0
- onecoder/commands/project.py +270 -0
- onecoder/commands/server.py +170 -0
- onecoder/config_manager.py +87 -0
- onecoder/constants.py +9 -0
- onecoder/diagnostics/__init__.py +2 -0
- onecoder/diagnostics/env_scan.py +207 -0
- onecoder/discovery.py +101 -0
- onecoder/distillation.py +236 -0
- onecoder/evaluation/__init__.py +1 -0
- onecoder/evaluation/ttu.py +176 -0
- onecoder/governance/__init__.py +0 -0
- onecoder/governance/probllm.py +91 -0
- onecoder/hooks.py +74 -0
- onecoder/ipc_auth.py +200 -0
- onecoder/issues.py +188 -0
- onecoder/jules_client.py +343 -0
- onecoder/knowledge.py +106 -0
- onecoder/llm.py +61 -0
- onecoder/logger.py +42 -0
- onecoder/metrics.py +129 -0
- onecoder/models/delegation.py +46 -0
- onecoder/onboarding.py +264 -0
- onecoder/review.py +233 -0
- onecoder/services/delegation_service.py +209 -0
- onecoder/services/validation_service.py +104 -0
- onecoder/sessions.py +186 -0
- onecoder/sprint_collector.py +165 -0
- onecoder/sync.py +167 -0
- onecoder/tmux.py +86 -0
- onecoder/tools/__init__.py +10 -0
- onecoder/tools/executor.py +53 -0
- onecoder/tools/external_tools.py +106 -0
- onecoder/tools/file_tools.py +77 -0
- onecoder/tools/interface.py +25 -0
- onecoder/tools/jules_tools.py +122 -0
- onecoder/tools/kit_tools.py +122 -0
- onecoder/tools/registry.py +32 -0
- onecoder/tui/__init__.py +5 -0
- onecoder/tui/app.py +263 -0
- onecoder/tui/commands.py +150 -0
- onecoder/tui/widgets.py +92 -0
- onecoder/worktree.py +186 -0
- onecoder-0.0.2.dist-info/METADATA +17 -0
- onecoder-0.0.2.dist-info/RECORD +73 -0
- onecoder-0.0.2.dist-info/WHEEL +5 -0
- onecoder-0.0.2.dist-info/entry_points.txt +2 -0
- onecoder-0.0.2.dist-info/top_level.txt +1 -0
onecoder/blackboard.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import datetime
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Any, Optional, Dict, List
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
class BlackboardMemory:
|
|
11
|
+
"""
|
|
12
|
+
A SQLite-backed shared memory (Blackboard) for agents.
|
|
13
|
+
Allows persistent key-value storage with scoping.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, db_path: str = ".adk/blackboard.db"):
|
|
17
|
+
self.db_path = Path(db_path)
|
|
18
|
+
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
19
|
+
self._init_db()
|
|
20
|
+
|
|
21
|
+
def _init_db(self):
|
|
22
|
+
with sqlite3.connect(self.db_path) as conn:
|
|
23
|
+
conn.execute("""
|
|
24
|
+
CREATE TABLE IF NOT EXISTS blackboard (
|
|
25
|
+
scope TEXT,
|
|
26
|
+
namespace TEXT,
|
|
27
|
+
key TEXT,
|
|
28
|
+
value TEXT,
|
|
29
|
+
updated_at DATETIME,
|
|
30
|
+
PRIMARY KEY (scope, namespace, key)
|
|
31
|
+
)
|
|
32
|
+
""")
|
|
33
|
+
conn.commit()
|
|
34
|
+
|
|
35
|
+
def set(self, key: str, value: Any, scope: str = "global", namespace: str = "shared"):
|
|
36
|
+
"""Sets a value in the blackboard."""
|
|
37
|
+
val_str = json.dumps(value)
|
|
38
|
+
now = datetime.datetime.now().isoformat()
|
|
39
|
+
with sqlite3.connect(self.db_path) as conn:
|
|
40
|
+
conn.execute("""
|
|
41
|
+
INSERT OR REPLACE INTO blackboard (scope, namespace, key, value, updated_at)
|
|
42
|
+
VALUES (?, ?, ?, ?, ?)
|
|
43
|
+
""", (scope, namespace, key, val_str, now))
|
|
44
|
+
conn.commit()
|
|
45
|
+
|
|
46
|
+
def get(self, key: str, scope: str = "global", namespace: str = "shared") -> Any:
|
|
47
|
+
"""Gets a value from the blackboard."""
|
|
48
|
+
with sqlite3.connect(self.db_path) as conn:
|
|
49
|
+
row = conn.execute("""
|
|
50
|
+
SELECT value FROM blackboard WHERE scope = ? AND namespace = ? AND key = ?
|
|
51
|
+
""", (scope, namespace, key)).fetchone()
|
|
52
|
+
if row:
|
|
53
|
+
return json.loads(row[0])
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
def delete(self, key: str, scope: str = "global", namespace: str = "shared"):
|
|
57
|
+
"""Deletes a key from the blackboard."""
|
|
58
|
+
with sqlite3.connect(self.db_path) as conn:
|
|
59
|
+
conn.execute("""
|
|
60
|
+
DELETE FROM blackboard WHERE scope = ? AND namespace = ? AND key = ?
|
|
61
|
+
""", (scope, namespace, key))
|
|
62
|
+
conn.commit()
|
|
63
|
+
|
|
64
|
+
def list_keys(self, scope: Optional[str] = None, namespace: Optional[str] = None) -> List[Dict[str, str]]:
|
|
65
|
+
"""Lists keys in the blackboard, optionally filtered by scope/namespace."""
|
|
66
|
+
query = "SELECT scope, namespace, key, updated_at FROM blackboard"
|
|
67
|
+
params = []
|
|
68
|
+
if scope and namespace:
|
|
69
|
+
query += " WHERE scope = ? AND namespace = ?"
|
|
70
|
+
params = [scope, namespace]
|
|
71
|
+
elif scope:
|
|
72
|
+
query += " WHERE scope = ?"
|
|
73
|
+
params = [scope]
|
|
74
|
+
elif namespace:
|
|
75
|
+
query += " WHERE namespace = ?"
|
|
76
|
+
params = [namespace]
|
|
77
|
+
|
|
78
|
+
with sqlite3.connect(self.db_path) as conn:
|
|
79
|
+
rows = conn.execute(query, params).fetchall()
|
|
80
|
+
return [
|
|
81
|
+
{"scope": r[0], "namespace": r[1], "key": r[2], "updated_at": r[3]}
|
|
82
|
+
for r in rows
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
def get_all(self, scope: str = "global", namespace: str = "shared") -> Dict[str, Any]:
|
|
86
|
+
"""Gets all key-value pairs for a specific scope and namespace."""
|
|
87
|
+
with sqlite3.connect(self.db_path) as conn:
|
|
88
|
+
rows = conn.execute("""
|
|
89
|
+
SELECT key, value FROM blackboard WHERE scope = ? AND namespace = ?
|
|
90
|
+
""", (scope, namespace)).fetchall()
|
|
91
|
+
return {r[0]: json.loads(r[1]) for r in rows}
|
|
92
|
+
|
|
93
|
+
if __name__ == "__main__":
|
|
94
|
+
# Quick test
|
|
95
|
+
bb = BlackboardMemory("test_blackboard.db")
|
|
96
|
+
bb.set("status", "scanning", scope="sprint-025")
|
|
97
|
+
print(f"Status: {bb.get('status', scope='sprint-025')}")
|
|
98
|
+
print(f"All keys: {bb.list_keys()}")
|
|
99
|
+
bb.delete("status", scope="sprint-025")
|
|
100
|
+
import os
|
|
101
|
+
if os.path.exists("test_blackboard.db"):
|
|
102
|
+
os.remove("test_blackboard.db")
|
onecoder/cli.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from dotenv import load_dotenv
|
|
6
|
+
|
|
7
|
+
# Load environment variables
|
|
8
|
+
# env_path = Path(__file__).resolve().parent.parent / ".env"
|
|
9
|
+
# load_dotenv(env_path)
|
|
10
|
+
|
|
11
|
+
from .commands.auth import login, logout, whoami
|
|
12
|
+
from .commands.server import serve, web, tui
|
|
13
|
+
from .commands.issue import issue
|
|
14
|
+
from .commands.logs import logs
|
|
15
|
+
from .commands.project import (
|
|
16
|
+
init, status, knowledge, distill, sync, alignment, sprint_suggest
|
|
17
|
+
)
|
|
18
|
+
from .commands.delegate import (
|
|
19
|
+
delegate, delegate_list, delegate_status, delegate_validate, delegate_finish, jules_sessions
|
|
20
|
+
)
|
|
21
|
+
from .commands.doctor import doctor
|
|
22
|
+
from .commands.ci import ci
|
|
23
|
+
from .review import CodeReviewer
|
|
24
|
+
|
|
25
|
+
from .logger import configure_logging
|
|
26
|
+
|
|
27
|
+
@click.group()
|
|
28
|
+
@click.version_option(version="0.0.2", prog_name="onecoder")
|
|
29
|
+
@click.option("--verbose", is_flag=True, help="Enable verbose logging.")
|
|
30
|
+
def cli(verbose):
|
|
31
|
+
"""OneCoder: Unified Agent Architecture."""
|
|
32
|
+
configure_logging(verbose=verbose)
|
|
33
|
+
|
|
34
|
+
def main():
|
|
35
|
+
"""Main entry point with telemetry wrapper."""
|
|
36
|
+
try:
|
|
37
|
+
cli()
|
|
38
|
+
except Exception as e:
|
|
39
|
+
# Don't capture Click-internal exit exceptions as failures
|
|
40
|
+
if isinstance(e, (click.exceptions.Exit, click.exceptions.Abort, click.exceptions.ClickException)):
|
|
41
|
+
raise e
|
|
42
|
+
|
|
43
|
+
from ai_sprint.telemetry import FailureModeCapture
|
|
44
|
+
capture = FailureModeCapture()
|
|
45
|
+
capture.capture(e, context={"command_args": sys.argv[1:]})
|
|
46
|
+
raise e
|
|
47
|
+
|
|
48
|
+
# Register Commands
|
|
49
|
+
def is_internal_features_enabled():
|
|
50
|
+
"""Check if internal features should be enabled."""
|
|
51
|
+
return os.getenv("ONECODER_INTERNAL", "false").lower() == "true" or \
|
|
52
|
+
os.getenv("ONE_CODER_DEV", "false").lower() == "true"
|
|
53
|
+
|
|
54
|
+
# Register Commands
|
|
55
|
+
cli.add_command(login)
|
|
56
|
+
cli.add_command(logout)
|
|
57
|
+
cli.add_command(whoami)
|
|
58
|
+
|
|
59
|
+
cli.add_command(serve)
|
|
60
|
+
cli.add_command(web)
|
|
61
|
+
cli.add_command(tui)
|
|
62
|
+
|
|
63
|
+
cli.add_command(issue)
|
|
64
|
+
cli.add_command(logs)
|
|
65
|
+
cli.add_command(doctor)
|
|
66
|
+
|
|
67
|
+
cli.add_command(init)
|
|
68
|
+
cli.add_command(status)
|
|
69
|
+
cli.add_command(knowledge)
|
|
70
|
+
cli.add_command(distill)
|
|
71
|
+
cli.add_command(sync)
|
|
72
|
+
cli.add_command(alignment)
|
|
73
|
+
cli.add_command(ci)
|
|
74
|
+
|
|
75
|
+
if is_internal_features_enabled():
|
|
76
|
+
cli.add_command(delegate)
|
|
77
|
+
cli.add_command(delegate_list)
|
|
78
|
+
cli.add_command(delegate_status)
|
|
79
|
+
cli.add_command(delegate_validate)
|
|
80
|
+
cli.add_command(delegate_finish)
|
|
81
|
+
cli.add_command(jules_sessions)
|
|
82
|
+
|
|
83
|
+
@cli.command()
|
|
84
|
+
@click.argument("pr_id", required=False)
|
|
85
|
+
@click.option("--local", is_flag=True, help="Review local changes against main")
|
|
86
|
+
def review(pr_id, local):
|
|
87
|
+
"""Run a policy-grounded AI review on a PR or local code."""
|
|
88
|
+
reviewer = CodeReviewer()
|
|
89
|
+
reviewer.review(pr_id=pr_id, local=local)
|
|
90
|
+
|
|
91
|
+
# Sprint Group Integration
|
|
92
|
+
if is_internal_features_enabled():
|
|
93
|
+
try:
|
|
94
|
+
from ai_sprint.cli import main as sprint_main
|
|
95
|
+
# Attempt to add the command to the existing group
|
|
96
|
+
if sprint_suggest not in sprint_main.commands.values():
|
|
97
|
+
sprint_main.add_command(sprint_suggest)
|
|
98
|
+
cli.add_command(sprint_main, name="sprint")
|
|
99
|
+
except ImportError:
|
|
100
|
+
@cli.group(name="sprint")
|
|
101
|
+
def sprint_group():
|
|
102
|
+
"""Sprint management commands."""
|
|
103
|
+
pass
|
|
104
|
+
sprint_group.add_command(sprint_suggest)
|
|
105
|
+
cli.add_command(sprint_group)
|
|
106
|
+
|
|
107
|
+
if __name__ == "__main__":
|
|
108
|
+
cli()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Command package
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import os
|
|
3
|
+
import asyncio
|
|
4
|
+
import webbrowser
|
|
5
|
+
from functools import wraps
|
|
6
|
+
from ..api_client import get_api_client
|
|
7
|
+
from ..config_manager import config_manager
|
|
8
|
+
|
|
9
|
+
def require_login(f):
|
|
10
|
+
"""Decorator to enforce login."""
|
|
11
|
+
@wraps(f)
|
|
12
|
+
def wrapper(*args, **kwargs):
|
|
13
|
+
token = config_manager.get_token()
|
|
14
|
+
if not token:
|
|
15
|
+
click.echo("Error: You must be logged in to run this command.")
|
|
16
|
+
click.echo("Run 'onecoder login' first.")
|
|
17
|
+
return
|
|
18
|
+
return f(*args, **kwargs)
|
|
19
|
+
return wrapper
|
|
20
|
+
|
|
21
|
+
from ..constants import GITHUB_CLIENT_ID
|
|
22
|
+
|
|
23
|
+
@click.command()
|
|
24
|
+
def login():
|
|
25
|
+
"""Authenticates with OneCoder via GitHub."""
|
|
26
|
+
client_id = GITHUB_CLIENT_ID
|
|
27
|
+
|
|
28
|
+
# Debug info
|
|
29
|
+
if client_id == "your_github_client_id":
|
|
30
|
+
click.secho(f"Debug: CWD is {os.getcwd()}", fg="yellow")
|
|
31
|
+
click.secho("Debug: GITHUB_CLIENT_ID is using placeholder!", fg="red")
|
|
32
|
+
|
|
33
|
+
auth_url = f"https://github.com/login/oauth/authorize?client_id={client_id}&scope=user:email"
|
|
34
|
+
|
|
35
|
+
click.echo("To authenticate, please visit the following URL in your browser:")
|
|
36
|
+
click.echo(f"\n {auth_url}\n")
|
|
37
|
+
click.secho("Note: If you haven't installed the OneCoder GitHub App yet, please do so from the App Profile page.", fg="yellow")
|
|
38
|
+
|
|
39
|
+
if click.confirm("Open browser automatically?", default=True):
|
|
40
|
+
webbrowser.open(auth_url)
|
|
41
|
+
|
|
42
|
+
code = click.prompt("Enter the authorization code provided by GitHub")
|
|
43
|
+
|
|
44
|
+
async def do_login():
|
|
45
|
+
try:
|
|
46
|
+
client = get_api_client()
|
|
47
|
+
result = await client.login_with_github(code)
|
|
48
|
+
config_manager.set_token(result["token"])
|
|
49
|
+
config_manager.set_user(result["user"])
|
|
50
|
+
click.echo(f"Successfully logged in as {result['user']['username']}!")
|
|
51
|
+
|
|
52
|
+
if "github" in result and result["github"].get("expiresIn"):
|
|
53
|
+
click.echo(f" (Token expires in {result['github']['expiresIn']} seconds)")
|
|
54
|
+
|
|
55
|
+
except Exception as e:
|
|
56
|
+
import logging
|
|
57
|
+
logging.exception("Login process failed")
|
|
58
|
+
click.echo(f"Error: Login failed: {e}")
|
|
59
|
+
|
|
60
|
+
asyncio.run(do_login())
|
|
61
|
+
|
|
62
|
+
@click.command()
|
|
63
|
+
def logout():
|
|
64
|
+
"""Logs out of OneCoder."""
|
|
65
|
+
config_manager.clear_token()
|
|
66
|
+
click.echo("Successfully logged out.")
|
|
67
|
+
|
|
68
|
+
@click.command()
|
|
69
|
+
def whoami():
|
|
70
|
+
"""Shows the currently authenticated user."""
|
|
71
|
+
user = config_manager.get_user()
|
|
72
|
+
if user:
|
|
73
|
+
click.echo(f"Logged in as: {user['username']}")
|
|
74
|
+
token = config_manager.get_token()
|
|
75
|
+
if token:
|
|
76
|
+
click.echo(f"Token: {token[:10]}...{token[-10:]}")
|
|
77
|
+
else:
|
|
78
|
+
click.echo("Not logged in.")
|
onecoder/commands/ci.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
@click.command(context_settings=dict(ignore_unknown_options=True, allow_extra_args=True))
|
|
7
|
+
@click.argument("args", nargs=-1, type=click.UNPROCESSED)
|
|
8
|
+
def ci(args):
|
|
9
|
+
"""Run local CI/CD workflows using OneCoder CI."""
|
|
10
|
+
# Assumption: User runs this from the repo root where `scripts/onecoder-ci.sh` exists.
|
|
11
|
+
# Future improvement: Auto-detect repo root.
|
|
12
|
+
script_path = Path("scripts/onecoder-ci.sh")
|
|
13
|
+
|
|
14
|
+
if not script_path.exists():
|
|
15
|
+
click.echo("❌ Error: 'scripts/onecoder-ci.sh' not found.")
|
|
16
|
+
click.echo(" Please run this command from the root of the 'platform' repository.")
|
|
17
|
+
sys.exit(1)
|
|
18
|
+
|
|
19
|
+
# Prepare command: bash scripts/onecoder-ci.sh [args]
|
|
20
|
+
cmd = ["bash", str(script_path)] + list(args)
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
# Use subprocess.call or run to stream output directly
|
|
24
|
+
subprocess.run(cmd, check=True)
|
|
25
|
+
except subprocess.CalledProcessError as e:
|
|
26
|
+
sys.exit(e.returncode)
|
|
27
|
+
except Exception as e:
|
|
28
|
+
click.echo(f"❌ Error executing OneCoder CI: {str(e)}")
|
|
29
|
+
sys.exit(1)
|