sugarai 3.7.0.dev0__tar.gz → 3.7.0.dev1__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.
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/PKG-INFO +1 -1
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/pyproject.toml +1 -1
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/main.py +37 -21
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/memory/store.py +44 -22
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/orchestration/task_orchestrator.py +13 -3
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/storage/issue_response_manager.py +8 -4
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/storage/task_type_manager.py +4 -2
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/storage/work_queue.py +38 -18
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugarai.egg-info/PKG-INFO +1 -1
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_opencode_integration.py +72 -71
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_storage.py +4 -2
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/LICENSE +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/README.md +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/setup.cfg +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/__version__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/agent/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/agent/base.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/agent/hooks.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/agent/subagent_manager.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/agent/tools.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/billing/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/billing/api_keys.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/billing/tiers.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/billing/usage.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/config/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/config/issue_responder_config.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/config/prompt_config.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/core/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/core/loop.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/discovery/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/discovery/code_quality.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/discovery/error_monitor.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/discovery/github_watcher.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/discovery/test_coverage.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/executor/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/executor/agent_sdk_executor.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/executor/base.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/executor/claude_wrapper.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/executor/hooks.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/executor/structured_request.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/executor/thinking_display.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/integrations/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/integrations/github.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/integrations/opencode/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/integrations/opencode/client.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/integrations/opencode/config.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/integrations/opencode/injector.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/integrations/opencode/models.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/integrations/opencode/notifier.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/learning/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/learning/adaptive_scheduler.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/learning/feedback_processor.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/learning/learnings_writer.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/mcp/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/mcp/memory_server.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/mcp/server.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/mcp/task_server.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/memory/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/memory/embedder.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/memory/retriever.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/memory/types.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/orchestration/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/orchestration/agent_router.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/orchestration/model_router.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/profiles/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/profiles/base.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/profiles/default.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/profiles/issue_responder.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/coordinator.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/criteria_templates.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/diff_validator.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/evidence.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/failure_handler.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/functional_verifier.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/preflight_checks.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/success_criteria.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/test_validator.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/truth_enforcer.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/verification_gate.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/ralph/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/ralph/config.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/ralph/profile.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/ralph/signals.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/ralph/validator.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/storage/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/triage/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/triage/capability_scanner.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/triage/complexity_analyzer.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/triage/triage_analyzer.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/utils/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/utils/git_operations.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/utils/toon_encoder.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/workflow/__init__.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/workflow/orchestrator.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugarai.egg-info/SOURCES.txt +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugarai.egg-info/dependency_links.txt +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugarai.egg-info/entry_points.txt +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugarai.egg-info/requires.txt +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugarai.egg-info/top_level.txt +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_agent.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_bash_permissions.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_billing.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_cli.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_complexity_analyzer.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_core_loop.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_github_client.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_goose_integration.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_hold_functionality.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_hooks.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_issue_responder_cli.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_issue_response_integration.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_mcp_integration.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_mcp_server.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_mcp_task_server.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_memory.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_model_router.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_orchestration.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_profiles.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_prompt_config.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_quality_gates.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_ralph.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_subagent_manager.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_task_hooks.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_task_types.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_thinking_capture.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_toon_encoder.py +0 -0
- {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_v3_benchmarks.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sugarai
|
|
3
|
-
Version: 3.7.0.
|
|
3
|
+
Version: 3.7.0.dev1
|
|
4
4
|
Summary: 🍰 Sugar - The autonomous layer for AI coding agents. Manages task queues, runs 24/7, ships working code.
|
|
5
5
|
Author-email: Steven Leggett <contact@roboticforce.io>
|
|
6
6
|
License: AGPL-3.0-or-later
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sugarai"
|
|
7
|
-
version = "3.7.0.
|
|
7
|
+
version = "3.7.0.dev1"
|
|
8
8
|
description = "🍰 Sugar - The autonomous layer for AI coding agents. Manages task queues, runs 24/7, ships working code."
|
|
9
9
|
|
|
10
10
|
readme = "README.md"
|
|
@@ -1882,7 +1882,8 @@ def status(ctx):
|
|
|
1882
1882
|
def help():
|
|
1883
1883
|
"""Show comprehensive Sugar help and getting started guide"""
|
|
1884
1884
|
|
|
1885
|
-
click.echo(
|
|
1885
|
+
click.echo(
|
|
1886
|
+
"""
|
|
1886
1887
|
🍰 Sugar - The Autonomous Layer for AI Coding Agents
|
|
1887
1888
|
=====================================================
|
|
1888
1889
|
|
|
@@ -1993,7 +1994,8 @@ Complete documentation: docs/README.md
|
|
|
1993
1994
|
• By using Sugar, you agree to these terms and conditions
|
|
1994
1995
|
|
|
1995
1996
|
Ready to supercharge your development workflow? 🚀
|
|
1996
|
-
"""
|
|
1997
|
+
"""
|
|
1998
|
+
)
|
|
1997
1999
|
|
|
1998
2000
|
|
|
1999
2001
|
@cli.command()
|
|
@@ -2994,7 +2996,8 @@ def dedupe(ctx, dry_run):
|
|
|
2994
2996
|
|
|
2995
2997
|
async with aiosqlite.connect(work_queue.db_path) as db:
|
|
2996
2998
|
# Find duplicates - keep the earliest created one for each source_file
|
|
2997
|
-
cursor = await db.execute(
|
|
2999
|
+
cursor = await db.execute(
|
|
3000
|
+
"""
|
|
2998
3001
|
WITH ranked_items AS (
|
|
2999
3002
|
SELECT id, source_file, title, created_at,
|
|
3000
3003
|
ROW_NUMBER() OVER (PARTITION BY source_file ORDER BY created_at ASC) as rn
|
|
@@ -3005,7 +3008,8 @@ def dedupe(ctx, dry_run):
|
|
|
3005
3008
|
FROM ranked_items
|
|
3006
3009
|
WHERE rn > 1
|
|
3007
3010
|
ORDER BY source_file, created_at
|
|
3008
|
-
"""
|
|
3011
|
+
"""
|
|
3012
|
+
)
|
|
3009
3013
|
|
|
3010
3014
|
duplicates = await cursor.fetchall()
|
|
3011
3015
|
|
|
@@ -4633,7 +4637,9 @@ def opencode_test(ctx, server):
|
|
|
4633
4637
|
click.echo("✅ Connection successful!")
|
|
4634
4638
|
return True
|
|
4635
4639
|
else:
|
|
4636
|
-
click.echo(
|
|
4640
|
+
click.echo(
|
|
4641
|
+
"❌ Server responded but health check failed (non-200 status)"
|
|
4642
|
+
)
|
|
4637
4643
|
click.echo(" Check that OpenCode is running correctly")
|
|
4638
4644
|
return False
|
|
4639
4645
|
except aiohttp.ClientConnectorError:
|
|
@@ -4719,8 +4725,12 @@ def opencode_notify(ctx, message, title, level):
|
|
|
4719
4725
|
|
|
4720
4726
|
@opencode.command("setup")
|
|
4721
4727
|
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompts")
|
|
4722
|
-
@click.option(
|
|
4723
|
-
|
|
4728
|
+
@click.option(
|
|
4729
|
+
"--dry-run", is_flag=True, help="Show what would be changed without modifying files"
|
|
4730
|
+
)
|
|
4731
|
+
@click.option(
|
|
4732
|
+
"--config", "config_path", type=click.Path(), help="Path to OpenCode config file"
|
|
4733
|
+
)
|
|
4724
4734
|
@click.option("--memory/--no-memory", default=True, help="Include memory MCP server")
|
|
4725
4735
|
@click.option("--tasks/--no-tasks", default=True, help="Include tasks MCP server")
|
|
4726
4736
|
@click.pass_context
|
|
@@ -4766,17 +4776,21 @@ def opencode_setup(ctx, yes, dry_run, config_path, memory, tasks):
|
|
|
4766
4776
|
candidates.append(Path(env_config_dir) / "opencode.jsonc")
|
|
4767
4777
|
|
|
4768
4778
|
# Check project-local config
|
|
4769
|
-
candidates.extend(
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4779
|
+
candidates.extend(
|
|
4780
|
+
[
|
|
4781
|
+
Path(".opencode") / "opencode.json",
|
|
4782
|
+
Path(".opencode") / "opencode.jsonc",
|
|
4783
|
+
]
|
|
4784
|
+
)
|
|
4773
4785
|
|
|
4774
4786
|
# Check user config directory
|
|
4775
4787
|
home = Path.home()
|
|
4776
|
-
candidates.extend(
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4788
|
+
candidates.extend(
|
|
4789
|
+
[
|
|
4790
|
+
home / ".config" / "opencode" / "opencode.json",
|
|
4791
|
+
home / ".config" / "opencode" / "opencode.jsonc",
|
|
4792
|
+
]
|
|
4793
|
+
)
|
|
4780
4794
|
|
|
4781
4795
|
for candidate in candidates:
|
|
4782
4796
|
if candidate.exists():
|
|
@@ -4795,11 +4809,11 @@ def opencode_setup(ctx, yes, dry_run, config_path, memory, tasks):
|
|
|
4795
4809
|
# Fall back to JSONC parsing
|
|
4796
4810
|
# Remove single-line comments (but not :// in URLs)
|
|
4797
4811
|
# Match // only when preceded by whitespace or start of line
|
|
4798
|
-
content = re.sub(r
|
|
4812
|
+
content = re.sub(r"(?<![:])\s*//.*$", "", content, flags=re.MULTILINE)
|
|
4799
4813
|
# Remove multi-line comments
|
|
4800
|
-
content = re.sub(r
|
|
4814
|
+
content = re.sub(r"/\*.*?\*/", "", content, flags=re.DOTALL)
|
|
4801
4815
|
# Remove trailing commas before } or ]
|
|
4802
|
-
content = re.sub(r
|
|
4816
|
+
content = re.sub(r",(\s*[}\]])", r"\1", content)
|
|
4803
4817
|
return json.loads(content)
|
|
4804
4818
|
|
|
4805
4819
|
def format_json(data):
|
|
@@ -4816,7 +4830,9 @@ def opencode_setup(ctx, yes, dry_run, config_path, memory, tasks):
|
|
|
4816
4830
|
click.echo(" - ~/.config/opencode/opencode.json (user)")
|
|
4817
4831
|
click.echo("\nTo create a config file:")
|
|
4818
4832
|
click.echo(" mkdir -p ~/.config/opencode")
|
|
4819
|
-
click.echo(
|
|
4833
|
+
click.echo(
|
|
4834
|
+
' echo \'{"$schema": "https://opencode.ai/config.json"}\' > ~/.config/opencode/opencode.json'
|
|
4835
|
+
)
|
|
4820
4836
|
click.echo("\nThen run: sugar opencode setup")
|
|
4821
4837
|
sys.exit(1)
|
|
4822
4838
|
|
|
@@ -4843,13 +4859,13 @@ def opencode_setup(ctx, yes, dry_run, config_path, memory, tasks):
|
|
|
4843
4859
|
if tasks:
|
|
4844
4860
|
mcp_servers["sugar-tasks"] = {
|
|
4845
4861
|
"type": "local",
|
|
4846
|
-
"command": ["sugar", "mcp", "tasks"]
|
|
4862
|
+
"command": ["sugar", "mcp", "tasks"],
|
|
4847
4863
|
}
|
|
4848
4864
|
|
|
4849
4865
|
if memory:
|
|
4850
4866
|
mcp_servers["sugar-memory"] = {
|
|
4851
4867
|
"type": "local",
|
|
4852
|
-
"command": ["sugar", "mcp", "memory"]
|
|
4868
|
+
"command": ["sugar", "mcp", "memory"],
|
|
4853
4869
|
}
|
|
4854
4870
|
|
|
4855
4871
|
if not mcp_servers:
|
|
@@ -97,7 +97,8 @@ class MemoryStore:
|
|
|
97
97
|
cursor = conn.cursor()
|
|
98
98
|
|
|
99
99
|
# Main memory entries table
|
|
100
|
-
cursor.execute(
|
|
100
|
+
cursor.execute(
|
|
101
|
+
"""
|
|
101
102
|
CREATE TABLE IF NOT EXISTS memory_entries (
|
|
102
103
|
id TEXT PRIMARY KEY,
|
|
103
104
|
memory_type TEXT NOT NULL,
|
|
@@ -111,24 +112,32 @@ class MemoryStore:
|
|
|
111
112
|
access_count INTEGER DEFAULT 0,
|
|
112
113
|
expires_at TIMESTAMP
|
|
113
114
|
)
|
|
114
|
-
"""
|
|
115
|
+
"""
|
|
116
|
+
)
|
|
115
117
|
|
|
116
118
|
# Indexes
|
|
117
|
-
cursor.execute(
|
|
119
|
+
cursor.execute(
|
|
120
|
+
"""
|
|
118
121
|
CREATE INDEX IF NOT EXISTS idx_memory_type
|
|
119
122
|
ON memory_entries(memory_type)
|
|
120
|
-
"""
|
|
121
|
-
|
|
123
|
+
"""
|
|
124
|
+
)
|
|
125
|
+
cursor.execute(
|
|
126
|
+
"""
|
|
122
127
|
CREATE INDEX IF NOT EXISTS idx_memory_importance
|
|
123
128
|
ON memory_entries(importance DESC)
|
|
124
|
-
"""
|
|
125
|
-
|
|
129
|
+
"""
|
|
130
|
+
)
|
|
131
|
+
cursor.execute(
|
|
132
|
+
"""
|
|
126
133
|
CREATE INDEX IF NOT EXISTS idx_memory_created
|
|
127
134
|
ON memory_entries(created_at DESC)
|
|
128
|
-
"""
|
|
135
|
+
"""
|
|
136
|
+
)
|
|
129
137
|
|
|
130
138
|
# FTS5 for keyword search (always available)
|
|
131
|
-
cursor.execute(
|
|
139
|
+
cursor.execute(
|
|
140
|
+
"""
|
|
132
141
|
CREATE VIRTUAL TABLE IF NOT EXISTS memory_fts USING fts5(
|
|
133
142
|
id,
|
|
134
143
|
content,
|
|
@@ -136,39 +145,48 @@ class MemoryStore:
|
|
|
136
145
|
content='memory_entries',
|
|
137
146
|
content_rowid='rowid'
|
|
138
147
|
)
|
|
139
|
-
"""
|
|
148
|
+
"""
|
|
149
|
+
)
|
|
140
150
|
|
|
141
151
|
# Triggers to keep FTS in sync
|
|
142
|
-
cursor.execute(
|
|
152
|
+
cursor.execute(
|
|
153
|
+
"""
|
|
143
154
|
CREATE TRIGGER IF NOT EXISTS memory_ai AFTER INSERT ON memory_entries BEGIN
|
|
144
155
|
INSERT INTO memory_fts(rowid, id, content, summary)
|
|
145
156
|
VALUES (new.rowid, new.id, new.content, new.summary);
|
|
146
157
|
END
|
|
147
|
-
"""
|
|
148
|
-
|
|
158
|
+
"""
|
|
159
|
+
)
|
|
160
|
+
cursor.execute(
|
|
161
|
+
"""
|
|
149
162
|
CREATE TRIGGER IF NOT EXISTS memory_ad AFTER DELETE ON memory_entries BEGIN
|
|
150
163
|
INSERT INTO memory_fts(memory_fts, rowid, id, content, summary)
|
|
151
164
|
VALUES ('delete', old.rowid, old.id, old.content, old.summary);
|
|
152
165
|
END
|
|
153
|
-
"""
|
|
154
|
-
|
|
166
|
+
"""
|
|
167
|
+
)
|
|
168
|
+
cursor.execute(
|
|
169
|
+
"""
|
|
155
170
|
CREATE TRIGGER IF NOT EXISTS memory_au AFTER UPDATE ON memory_entries BEGIN
|
|
156
171
|
INSERT INTO memory_fts(memory_fts, rowid, id, content, summary)
|
|
157
172
|
VALUES ('delete', old.rowid, old.id, old.content, old.summary);
|
|
158
173
|
INSERT INTO memory_fts(rowid, id, content, summary)
|
|
159
174
|
VALUES (new.rowid, new.id, new.content, new.summary);
|
|
160
175
|
END
|
|
161
|
-
"""
|
|
176
|
+
"""
|
|
177
|
+
)
|
|
162
178
|
|
|
163
179
|
# Vector storage table (if sqlite-vec available)
|
|
164
180
|
if self._has_vec:
|
|
165
181
|
try:
|
|
166
|
-
cursor.execute(
|
|
182
|
+
cursor.execute(
|
|
183
|
+
f"""
|
|
167
184
|
CREATE VIRTUAL TABLE IF NOT EXISTS memory_vectors USING vec0(
|
|
168
185
|
id TEXT PRIMARY KEY,
|
|
169
186
|
embedding float[{EMBEDDING_DIM}]
|
|
170
187
|
)
|
|
171
|
-
"""
|
|
188
|
+
"""
|
|
189
|
+
)
|
|
172
190
|
except Exception as e:
|
|
173
191
|
logger.warning(f"Failed to create vector table: {e}")
|
|
174
192
|
self._has_vec = False
|
|
@@ -584,20 +602,24 @@ class MemoryStore:
|
|
|
584
602
|
cursor = conn.cursor()
|
|
585
603
|
|
|
586
604
|
# Get IDs to delete (for vector cleanup)
|
|
587
|
-
cursor.execute(
|
|
605
|
+
cursor.execute(
|
|
606
|
+
"""
|
|
588
607
|
SELECT id FROM memory_entries
|
|
589
608
|
WHERE expires_at IS NOT NULL AND expires_at < datetime('now')
|
|
590
|
-
"""
|
|
609
|
+
"""
|
|
610
|
+
)
|
|
591
611
|
expired_ids = [row["id"] for row in cursor.fetchall()]
|
|
592
612
|
|
|
593
613
|
if not expired_ids:
|
|
594
614
|
return 0
|
|
595
615
|
|
|
596
616
|
# Delete from main table
|
|
597
|
-
cursor.execute(
|
|
617
|
+
cursor.execute(
|
|
618
|
+
"""
|
|
598
619
|
DELETE FROM memory_entries
|
|
599
620
|
WHERE expires_at IS NOT NULL AND expires_at < datetime('now')
|
|
600
|
-
"""
|
|
621
|
+
"""
|
|
622
|
+
)
|
|
601
623
|
deleted = cursor.rowcount
|
|
602
624
|
|
|
603
625
|
# Clean up vectors
|
|
@@ -721,7 +721,9 @@ class TaskOrchestrator:
|
|
|
721
721
|
"""
|
|
722
722
|
|
|
723
723
|
if stage == OrchestrationStage.RESEARCH:
|
|
724
|
-
return
|
|
724
|
+
return (
|
|
725
|
+
base_prompt
|
|
726
|
+
+ """
|
|
725
727
|
## Your Role
|
|
726
728
|
You are conducting research for this task. Your goals:
|
|
727
729
|
1. Search for relevant best practices and documentation
|
|
@@ -736,6 +738,7 @@ Provide a research summary covering:
|
|
|
736
738
|
- Technical requirements
|
|
737
739
|
- Recommendations for implementation
|
|
738
740
|
"""
|
|
741
|
+
)
|
|
739
742
|
|
|
740
743
|
elif stage == OrchestrationStage.PLANNING:
|
|
741
744
|
research_context = ""
|
|
@@ -744,7 +747,10 @@ Provide a research summary covering:
|
|
|
744
747
|
f"\n## Research Findings\n{context['research_output']}\n"
|
|
745
748
|
)
|
|
746
749
|
|
|
747
|
-
return
|
|
750
|
+
return (
|
|
751
|
+
base_prompt
|
|
752
|
+
+ research_context
|
|
753
|
+
+ """
|
|
748
754
|
## Your Role
|
|
749
755
|
You are creating an implementation plan for this task. Your goals:
|
|
750
756
|
1. Break down the task into manageable subtasks
|
|
@@ -769,12 +775,15 @@ Create a plan with subtasks in this format:
|
|
|
769
775
|
## Dependencies
|
|
770
776
|
Explain the order of execution and why.
|
|
771
777
|
"""
|
|
778
|
+
)
|
|
772
779
|
|
|
773
780
|
elif stage == OrchestrationStage.REVIEW:
|
|
774
781
|
impl_results = context.get("subtask_results", [])
|
|
775
782
|
files_modified = context.get("files_modified", [])
|
|
776
783
|
|
|
777
|
-
return
|
|
784
|
+
return (
|
|
785
|
+
base_prompt
|
|
786
|
+
+ f"""
|
|
778
787
|
## Implementation Complete
|
|
779
788
|
The following subtasks have been completed:
|
|
780
789
|
{json.dumps(impl_results, indent=2)}
|
|
@@ -797,6 +806,7 @@ Provide a review covering:
|
|
|
797
806
|
- Recommendations for improvement
|
|
798
807
|
- Overall assessment (pass/fail)
|
|
799
808
|
"""
|
|
809
|
+
)
|
|
800
810
|
|
|
801
811
|
else:
|
|
802
812
|
return base_prompt
|
|
@@ -26,7 +26,8 @@ class IssueResponseManager:
|
|
|
26
26
|
return
|
|
27
27
|
|
|
28
28
|
async with aiosqlite.connect(self.db_path) as db:
|
|
29
|
-
await db.execute(
|
|
29
|
+
await db.execute(
|
|
30
|
+
"""
|
|
30
31
|
CREATE TABLE IF NOT EXISTS issue_responses (
|
|
31
32
|
id TEXT PRIMARY KEY,
|
|
32
33
|
repo TEXT NOT NULL,
|
|
@@ -40,12 +41,15 @@ class IssueResponseManager:
|
|
|
40
41
|
was_auto_posted BOOLEAN DEFAULT 0,
|
|
41
42
|
UNIQUE(repo, issue_number, response_type)
|
|
42
43
|
)
|
|
43
|
-
"""
|
|
44
|
+
"""
|
|
45
|
+
)
|
|
44
46
|
|
|
45
|
-
await db.execute(
|
|
47
|
+
await db.execute(
|
|
48
|
+
"""
|
|
46
49
|
CREATE INDEX IF NOT EXISTS idx_issue_responses_repo_number
|
|
47
50
|
ON issue_responses (repo, issue_number)
|
|
48
|
-
"""
|
|
51
|
+
"""
|
|
52
|
+
)
|
|
49
53
|
|
|
50
54
|
await db.commit()
|
|
51
55
|
|
|
@@ -35,7 +35,8 @@ class TaskTypeManager:
|
|
|
35
35
|
|
|
36
36
|
if not table_exists:
|
|
37
37
|
# Create task_types table
|
|
38
|
-
await db.execute(
|
|
38
|
+
await db.execute(
|
|
39
|
+
"""
|
|
39
40
|
CREATE TABLE task_types (
|
|
40
41
|
id TEXT PRIMARY KEY,
|
|
41
42
|
name TEXT NOT NULL,
|
|
@@ -56,7 +57,8 @@ class TaskTypeManager:
|
|
|
56
57
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
57
58
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
58
59
|
)
|
|
59
|
-
"""
|
|
60
|
+
"""
|
|
61
|
+
)
|
|
60
62
|
|
|
61
63
|
# Populate with default types
|
|
62
64
|
default_types = self._get_default_task_types()
|
|
@@ -28,7 +28,8 @@ class WorkQueue:
|
|
|
28
28
|
return
|
|
29
29
|
|
|
30
30
|
async with aiosqlite.connect(self.db_path) as db:
|
|
31
|
-
await db.execute(
|
|
31
|
+
await db.execute(
|
|
32
|
+
"""
|
|
32
33
|
CREATE TABLE IF NOT EXISTS work_items (
|
|
33
34
|
id TEXT PRIMARY KEY,
|
|
34
35
|
type TEXT NOT NULL,
|
|
@@ -51,17 +52,22 @@ class WorkQueue:
|
|
|
51
52
|
total_elapsed_time REAL DEFAULT 0.0,
|
|
52
53
|
commit_sha TEXT
|
|
53
54
|
)
|
|
54
|
-
"""
|
|
55
|
+
"""
|
|
56
|
+
)
|
|
55
57
|
|
|
56
|
-
await db.execute(
|
|
58
|
+
await db.execute(
|
|
59
|
+
"""
|
|
57
60
|
CREATE INDEX IF NOT EXISTS idx_work_items_priority_status
|
|
58
61
|
ON work_items (priority ASC, status, created_at)
|
|
59
|
-
"""
|
|
62
|
+
"""
|
|
63
|
+
)
|
|
60
64
|
|
|
61
|
-
await db.execute(
|
|
65
|
+
await db.execute(
|
|
66
|
+
"""
|
|
62
67
|
CREATE INDEX IF NOT EXISTS idx_work_items_status
|
|
63
68
|
ON work_items (status)
|
|
64
|
-
"""
|
|
69
|
+
"""
|
|
70
|
+
)
|
|
65
71
|
|
|
66
72
|
# Migrate existing databases to add timing columns and task types table
|
|
67
73
|
await self._migrate_timing_columns(db)
|
|
@@ -120,7 +126,8 @@ class WorkQueue:
|
|
|
120
126
|
|
|
121
127
|
if not table_exists:
|
|
122
128
|
# Create task_types table
|
|
123
|
-
await db.execute(
|
|
129
|
+
await db.execute(
|
|
130
|
+
"""
|
|
124
131
|
CREATE TABLE task_types (
|
|
125
132
|
id TEXT PRIMARY KEY,
|
|
126
133
|
name TEXT NOT NULL,
|
|
@@ -133,7 +140,8 @@ class WorkQueue:
|
|
|
133
140
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
134
141
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
135
142
|
)
|
|
136
|
-
"""
|
|
143
|
+
"""
|
|
144
|
+
)
|
|
137
145
|
|
|
138
146
|
# Insert default task types
|
|
139
147
|
default_types = [
|
|
@@ -261,10 +269,12 @@ class WorkQueue:
|
|
|
261
269
|
logger.info("Added assigned_agent column to existing database")
|
|
262
270
|
|
|
263
271
|
# Create index for parent_task_id queries
|
|
264
|
-
await db.execute(
|
|
272
|
+
await db.execute(
|
|
273
|
+
"""
|
|
265
274
|
CREATE INDEX IF NOT EXISTS idx_work_items_parent_task_id
|
|
266
275
|
ON work_items (parent_task_id)
|
|
267
|
-
"""
|
|
276
|
+
"""
|
|
277
|
+
)
|
|
268
278
|
|
|
269
279
|
except Exception as e:
|
|
270
280
|
logger.warning(f"Orchestration migration warning (non-critical): {e}")
|
|
@@ -446,12 +456,14 @@ class WorkQueue:
|
|
|
446
456
|
db.row_factory = aiosqlite.Row
|
|
447
457
|
|
|
448
458
|
# Get highest priority pending work item (exclude hold status)
|
|
449
|
-
cursor = await db.execute(
|
|
459
|
+
cursor = await db.execute(
|
|
460
|
+
"""
|
|
450
461
|
SELECT * FROM work_items
|
|
451
462
|
WHERE status = 'pending'
|
|
452
463
|
ORDER BY priority ASC, created_at ASC
|
|
453
464
|
LIMIT 1
|
|
454
|
-
"""
|
|
465
|
+
"""
|
|
466
|
+
)
|
|
455
467
|
|
|
456
468
|
row = await cursor.fetchone()
|
|
457
469
|
|
|
@@ -694,11 +706,13 @@ class WorkQueue:
|
|
|
694
706
|
stats = {}
|
|
695
707
|
|
|
696
708
|
# Count by status
|
|
697
|
-
cursor = await db.execute(
|
|
709
|
+
cursor = await db.execute(
|
|
710
|
+
"""
|
|
698
711
|
SELECT status, COUNT(*) as count
|
|
699
712
|
FROM work_items
|
|
700
713
|
GROUP BY status
|
|
701
|
-
"""
|
|
714
|
+
"""
|
|
715
|
+
)
|
|
702
716
|
|
|
703
717
|
rows = await cursor.fetchall()
|
|
704
718
|
for row in rows:
|
|
@@ -712,10 +726,12 @@ class WorkQueue:
|
|
|
712
726
|
stats["total"] = sum(stats.values())
|
|
713
727
|
|
|
714
728
|
# Recent activity (last 24 hours)
|
|
715
|
-
cursor = await db.execute(
|
|
729
|
+
cursor = await db.execute(
|
|
730
|
+
"""
|
|
716
731
|
SELECT COUNT(*) FROM work_items
|
|
717
732
|
WHERE created_at > datetime('now', '-1 day')
|
|
718
|
-
"""
|
|
733
|
+
"""
|
|
734
|
+
)
|
|
719
735
|
stats["recent_24h"] = (await cursor.fetchone())[0]
|
|
720
736
|
|
|
721
737
|
return stats
|
|
@@ -723,11 +739,15 @@ class WorkQueue:
|
|
|
723
739
|
async def cleanup_old_items(self, days_old: int = 30):
|
|
724
740
|
"""Clean up old completed/failed items"""
|
|
725
741
|
async with aiosqlite.connect(self.db_path) as db:
|
|
726
|
-
cursor = await db.execute(
|
|
742
|
+
cursor = await db.execute(
|
|
743
|
+
"""
|
|
727
744
|
DELETE FROM work_items
|
|
728
745
|
WHERE status IN ('completed', 'failed')
|
|
729
746
|
AND created_at < datetime('now', '-{} days')
|
|
730
|
-
""".format(
|
|
747
|
+
""".format(
|
|
748
|
+
days_old
|
|
749
|
+
)
|
|
750
|
+
)
|
|
731
751
|
|
|
732
752
|
deleted_count = cursor.rowcount
|
|
733
753
|
await db.commit()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sugarai
|
|
3
|
-
Version: 3.7.0.
|
|
3
|
+
Version: 3.7.0.dev1
|
|
4
4
|
Summary: 🍰 Sugar - The autonomous layer for AI coding agents. Manages task queues, runs 24/7, ships working code.
|
|
5
5
|
Author-email: Steven Leggett <contact@roboticforce.io>
|
|
6
6
|
License: AGPL-3.0-or-later
|