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.
Files changed (129) hide show
  1. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/PKG-INFO +1 -1
  2. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/pyproject.toml +1 -1
  3. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/main.py +37 -21
  4. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/memory/store.py +44 -22
  5. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/orchestration/task_orchestrator.py +13 -3
  6. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/storage/issue_response_manager.py +8 -4
  7. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/storage/task_type_manager.py +4 -2
  8. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/storage/work_queue.py +38 -18
  9. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugarai.egg-info/PKG-INFO +1 -1
  10. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_opencode_integration.py +72 -71
  11. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_storage.py +4 -2
  12. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/LICENSE +0 -0
  13. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/README.md +0 -0
  14. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/setup.cfg +0 -0
  15. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/__init__.py +0 -0
  16. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/__version__.py +0 -0
  17. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/agent/__init__.py +0 -0
  18. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/agent/base.py +0 -0
  19. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/agent/hooks.py +0 -0
  20. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/agent/subagent_manager.py +0 -0
  21. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/agent/tools.py +0 -0
  22. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/billing/__init__.py +0 -0
  23. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/billing/api_keys.py +0 -0
  24. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/billing/tiers.py +0 -0
  25. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/billing/usage.py +0 -0
  26. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/config/__init__.py +0 -0
  27. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/config/issue_responder_config.py +0 -0
  28. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/config/prompt_config.py +0 -0
  29. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/core/__init__.py +0 -0
  30. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/core/loop.py +0 -0
  31. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/discovery/__init__.py +0 -0
  32. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/discovery/code_quality.py +0 -0
  33. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/discovery/error_monitor.py +0 -0
  34. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/discovery/github_watcher.py +0 -0
  35. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/discovery/test_coverage.py +0 -0
  36. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/executor/__init__.py +0 -0
  37. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/executor/agent_sdk_executor.py +0 -0
  38. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/executor/base.py +0 -0
  39. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/executor/claude_wrapper.py +0 -0
  40. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/executor/hooks.py +0 -0
  41. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/executor/structured_request.py +0 -0
  42. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/executor/thinking_display.py +0 -0
  43. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/integrations/__init__.py +0 -0
  44. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/integrations/github.py +0 -0
  45. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/integrations/opencode/__init__.py +0 -0
  46. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/integrations/opencode/client.py +0 -0
  47. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/integrations/opencode/config.py +0 -0
  48. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/integrations/opencode/injector.py +0 -0
  49. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/integrations/opencode/models.py +0 -0
  50. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/integrations/opencode/notifier.py +0 -0
  51. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/learning/__init__.py +0 -0
  52. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/learning/adaptive_scheduler.py +0 -0
  53. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/learning/feedback_processor.py +0 -0
  54. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/learning/learnings_writer.py +0 -0
  55. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/mcp/__init__.py +0 -0
  56. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/mcp/memory_server.py +0 -0
  57. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/mcp/server.py +0 -0
  58. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/mcp/task_server.py +0 -0
  59. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/memory/__init__.py +0 -0
  60. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/memory/embedder.py +0 -0
  61. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/memory/retriever.py +0 -0
  62. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/memory/types.py +0 -0
  63. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/orchestration/__init__.py +0 -0
  64. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/orchestration/agent_router.py +0 -0
  65. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/orchestration/model_router.py +0 -0
  66. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/profiles/__init__.py +0 -0
  67. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/profiles/base.py +0 -0
  68. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/profiles/default.py +0 -0
  69. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/profiles/issue_responder.py +0 -0
  70. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/__init__.py +0 -0
  71. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/coordinator.py +0 -0
  72. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/criteria_templates.py +0 -0
  73. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/diff_validator.py +0 -0
  74. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/evidence.py +0 -0
  75. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/failure_handler.py +0 -0
  76. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/functional_verifier.py +0 -0
  77. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/preflight_checks.py +0 -0
  78. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/success_criteria.py +0 -0
  79. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/test_validator.py +0 -0
  80. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/truth_enforcer.py +0 -0
  81. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/quality_gates/verification_gate.py +0 -0
  82. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/ralph/__init__.py +0 -0
  83. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/ralph/config.py +0 -0
  84. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/ralph/profile.py +0 -0
  85. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/ralph/signals.py +0 -0
  86. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/ralph/validator.py +0 -0
  87. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/storage/__init__.py +0 -0
  88. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/triage/__init__.py +0 -0
  89. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/triage/capability_scanner.py +0 -0
  90. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/triage/complexity_analyzer.py +0 -0
  91. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/triage/triage_analyzer.py +0 -0
  92. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/utils/__init__.py +0 -0
  93. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/utils/git_operations.py +0 -0
  94. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/utils/toon_encoder.py +0 -0
  95. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/workflow/__init__.py +0 -0
  96. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugar/workflow/orchestrator.py +0 -0
  97. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugarai.egg-info/SOURCES.txt +0 -0
  98. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugarai.egg-info/dependency_links.txt +0 -0
  99. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugarai.egg-info/entry_points.txt +0 -0
  100. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugarai.egg-info/requires.txt +0 -0
  101. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/sugarai.egg-info/top_level.txt +0 -0
  102. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_agent.py +0 -0
  103. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_bash_permissions.py +0 -0
  104. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_billing.py +0 -0
  105. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_cli.py +0 -0
  106. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_complexity_analyzer.py +0 -0
  107. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_core_loop.py +0 -0
  108. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_github_client.py +0 -0
  109. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_goose_integration.py +0 -0
  110. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_hold_functionality.py +0 -0
  111. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_hooks.py +0 -0
  112. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_issue_responder_cli.py +0 -0
  113. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_issue_response_integration.py +0 -0
  114. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_mcp_integration.py +0 -0
  115. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_mcp_server.py +0 -0
  116. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_mcp_task_server.py +0 -0
  117. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_memory.py +0 -0
  118. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_model_router.py +0 -0
  119. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_orchestration.py +0 -0
  120. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_profiles.py +0 -0
  121. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_prompt_config.py +0 -0
  122. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_quality_gates.py +0 -0
  123. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_ralph.py +0 -0
  124. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_subagent_manager.py +0 -0
  125. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_task_hooks.py +0 -0
  126. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_task_types.py +0 -0
  127. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_thinking_capture.py +0 -0
  128. {sugarai-3.7.0.dev0 → sugarai-3.7.0.dev1}/tests/test_toon_encoder.py +0 -0
  129. {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.dev0
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.dev0"
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("❌ Server responded but health check failed (non-200 status)")
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("--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")
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
- Path(".opencode") / "opencode.json",
4771
- Path(".opencode") / "opencode.jsonc",
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
- home / ".config" / "opencode" / "opencode.json",
4778
- home / ".config" / "opencode" / "opencode.jsonc",
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'(?<![:])\s*//.*$', '', content, flags=re.MULTILINE)
4812
+ content = re.sub(r"(?<![:])\s*//.*$", "", content, flags=re.MULTILINE)
4799
4813
  # Remove multi-line comments
4800
- content = re.sub(r'/\*.*?\*/', '', content, flags=re.DOTALL)
4814
+ content = re.sub(r"/\*.*?\*/", "", content, flags=re.DOTALL)
4801
4815
  # Remove trailing commas before } or ]
4802
- content = re.sub(r',(\s*[}\]])', r'\1', content)
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(' echo \'{"$schema": "https://opencode.ai/config.json"}\' > ~/.config/opencode/opencode.json')
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
- cursor.execute("""
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
- cursor.execute("""
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
- cursor.execute("""
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
- cursor.execute("""
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(f"""
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 base_prompt + """
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 base_prompt + research_context + """
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 base_prompt + f"""
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(days_old))
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.dev0
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