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