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