attune-ai 2.1.5__py3-none-any.whl → 2.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. attune/cli/__init__.py +3 -59
  2. attune/cli/commands/batch.py +4 -12
  3. attune/cli/commands/cache.py +8 -16
  4. attune/cli/commands/provider.py +17 -0
  5. attune/cli/commands/routing.py +3 -1
  6. attune/cli/commands/setup.py +122 -0
  7. attune/cli/commands/tier.py +1 -3
  8. attune/cli/commands/workflow.py +31 -0
  9. attune/cli/parsers/cache.py +1 -0
  10. attune/cli/parsers/help.py +1 -3
  11. attune/cli/parsers/provider.py +7 -0
  12. attune/cli/parsers/routing.py +1 -3
  13. attune/cli/parsers/setup.py +7 -0
  14. attune/cli/parsers/status.py +1 -3
  15. attune/cli/parsers/tier.py +1 -3
  16. attune/cli_minimal.py +9 -3
  17. attune/cli_router.py +9 -7
  18. attune/cli_unified.py +3 -0
  19. attune/dashboard/app.py +3 -1
  20. attune/dashboard/simple_server.py +3 -1
  21. attune/dashboard/standalone_server.py +7 -3
  22. attune/mcp/server.py +54 -102
  23. attune/memory/long_term.py +0 -2
  24. attune/memory/short_term/__init__.py +84 -0
  25. attune/memory/short_term/base.py +465 -0
  26. attune/memory/short_term/batch.py +219 -0
  27. attune/memory/short_term/caching.py +227 -0
  28. attune/memory/short_term/conflicts.py +265 -0
  29. attune/memory/short_term/cross_session.py +122 -0
  30. attune/memory/short_term/facade.py +653 -0
  31. attune/memory/short_term/pagination.py +207 -0
  32. attune/memory/short_term/patterns.py +271 -0
  33. attune/memory/short_term/pubsub.py +286 -0
  34. attune/memory/short_term/queues.py +244 -0
  35. attune/memory/short_term/security.py +300 -0
  36. attune/memory/short_term/sessions.py +250 -0
  37. attune/memory/short_term/streams.py +242 -0
  38. attune/memory/short_term/timelines.py +234 -0
  39. attune/memory/short_term/transactions.py +184 -0
  40. attune/memory/short_term/working.py +252 -0
  41. attune/meta_workflows/cli_commands/__init__.py +3 -0
  42. attune/meta_workflows/cli_commands/agent_commands.py +0 -4
  43. attune/meta_workflows/cli_commands/analytics_commands.py +0 -6
  44. attune/meta_workflows/cli_commands/config_commands.py +0 -5
  45. attune/meta_workflows/cli_commands/memory_commands.py +0 -5
  46. attune/meta_workflows/cli_commands/template_commands.py +0 -5
  47. attune/meta_workflows/cli_commands/workflow_commands.py +0 -6
  48. attune/meta_workflows/plan_generator.py +2 -4
  49. attune/models/adaptive_routing.py +4 -8
  50. attune/models/auth_cli.py +3 -9
  51. attune/models/auth_strategy.py +2 -4
  52. attune/models/telemetry/analytics.py +0 -2
  53. attune/models/telemetry/backend.py +0 -3
  54. attune/models/telemetry/storage.py +0 -2
  55. attune/monitoring/alerts.py +6 -10
  56. attune/orchestration/_strategies/__init__.py +156 -0
  57. attune/orchestration/_strategies/base.py +227 -0
  58. attune/orchestration/_strategies/conditional_strategies.py +365 -0
  59. attune/orchestration/_strategies/conditions.py +369 -0
  60. attune/orchestration/_strategies/core_strategies.py +479 -0
  61. attune/orchestration/_strategies/data_classes.py +64 -0
  62. attune/orchestration/_strategies/nesting.py +233 -0
  63. attune/orchestration/execution_strategies.py +58 -1567
  64. attune/orchestration/meta_orchestrator.py +1 -3
  65. attune/project_index/scanner.py +1 -3
  66. attune/project_index/scanner_parallel.py +7 -5
  67. attune/socratic/storage.py +2 -4
  68. attune/socratic_router.py +1 -3
  69. attune/telemetry/agent_coordination.py +9 -3
  70. attune/telemetry/agent_tracking.py +16 -3
  71. attune/telemetry/approval_gates.py +22 -5
  72. attune/telemetry/cli.py +1 -3
  73. attune/telemetry/commands/dashboard_commands.py +24 -8
  74. attune/telemetry/event_streaming.py +8 -2
  75. attune/telemetry/feedback_loop.py +10 -2
  76. attune/tools.py +2 -1
  77. attune/workflow_commands.py +1 -3
  78. attune/workflow_patterns/structural.py +4 -8
  79. attune/workflows/__init__.py +54 -10
  80. attune/workflows/autonomous_test_gen.py +158 -102
  81. attune/workflows/base.py +48 -672
  82. attune/workflows/batch_processing.py +1 -3
  83. attune/workflows/compat.py +156 -0
  84. attune/workflows/cost_mixin.py +141 -0
  85. attune/workflows/data_classes.py +92 -0
  86. attune/workflows/document_gen/workflow.py +11 -14
  87. attune/workflows/history.py +16 -9
  88. attune/workflows/llm_base.py +1 -3
  89. attune/workflows/migration.py +432 -0
  90. attune/workflows/output.py +2 -7
  91. attune/workflows/parsing_mixin.py +427 -0
  92. attune/workflows/perf_audit.py +3 -1
  93. attune/workflows/progress.py +9 -11
  94. attune/workflows/release_prep.py +5 -1
  95. attune/workflows/routing.py +0 -2
  96. attune/workflows/secure_release.py +4 -1
  97. attune/workflows/security_audit.py +20 -14
  98. attune/workflows/security_audit_phase3.py +28 -22
  99. attune/workflows/seo_optimization.py +27 -27
  100. attune/workflows/test_gen/test_templates.py +1 -4
  101. attune/workflows/test_gen/workflow.py +0 -2
  102. attune/workflows/test_gen_behavioral.py +6 -19
  103. attune/workflows/test_gen_parallel.py +8 -6
  104. {attune_ai-2.1.5.dist-info → attune_ai-2.2.1.dist-info}/METADATA +4 -3
  105. {attune_ai-2.1.5.dist-info → attune_ai-2.2.1.dist-info}/RECORD +121 -96
  106. {attune_ai-2.1.5.dist-info → attune_ai-2.2.1.dist-info}/entry_points.txt +0 -2
  107. attune_healthcare/monitors/monitoring/__init__.py +9 -9
  108. attune_llm/agent_factory/__init__.py +6 -6
  109. attune_llm/agent_factory/adapters/haystack_adapter.py +1 -4
  110. attune_llm/commands/__init__.py +10 -10
  111. attune_llm/commands/models.py +3 -3
  112. attune_llm/config/__init__.py +8 -8
  113. attune_llm/learning/__init__.py +3 -3
  114. attune_llm/learning/extractor.py +5 -3
  115. attune_llm/learning/storage.py +5 -3
  116. attune_llm/security/__init__.py +17 -17
  117. attune_llm/utils/tokens.py +3 -1
  118. attune/cli_legacy.py +0 -3978
  119. attune/memory/short_term.py +0 -2192
  120. attune/workflows/manage_docs.py +0 -87
  121. attune/workflows/test5.py +0 -125
  122. {attune_ai-2.1.5.dist-info → attune_ai-2.2.1.dist-info}/WHEEL +0 -0
  123. {attune_ai-2.1.5.dist-info → attune_ai-2.2.1.dist-info}/licenses/LICENSE +0 -0
  124. {attune_ai-2.1.5.dist-info → attune_ai-2.2.1.dist-info}/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +0 -0
  125. {attune_ai-2.1.5.dist-info → attune_ai-2.2.1.dist-info}/top_level.txt +0 -0
@@ -57,13 +57,15 @@ class EvalExecDetector(ast.NodeVisitor):
57
57
 
58
58
  if func_name in ("eval", "exec"):
59
59
  # Found a real eval/exec call!
60
- self.findings.append({
61
- "type": "command_injection",
62
- "function": func_name,
63
- "line": node.lineno,
64
- "col": node.col_offset,
65
- "context": self._current_function,
66
- })
60
+ self.findings.append(
61
+ {
62
+ "type": "command_injection",
63
+ "function": func_name,
64
+ "line": node.lineno,
65
+ "col": node.col_offset,
66
+ "context": self._current_function,
67
+ }
68
+ )
67
69
 
68
70
  self.generic_visit(node)
69
71
 
@@ -125,12 +127,10 @@ def is_scanner_implementation_file(file_path: str) -> bool:
125
127
  "owasp",
126
128
  "secrets_detector",
127
129
  "pii_scrubber",
128
-
129
130
  # Pattern/rule definition files
130
131
  "patterns.py",
131
132
  "rules.py",
132
133
  "checks.py",
133
-
134
134
  # Test files for security scanners
135
135
  "test_bug_predict",
136
136
  "test_security",
@@ -161,7 +161,11 @@ def is_in_docstring_or_comment(line_content: str, file_content: str, line_num: i
161
161
  return True
162
162
 
163
163
  # Check for inline comments
164
- if "#" in line_content and line_content.index("#") < line_content.find("eval") if "eval" in line_content else True:
164
+ if (
165
+ "#" in line_content and line_content.index("#") < line_content.find("eval")
166
+ if "eval" in line_content
167
+ else True
168
+ ):
165
169
  return True
166
170
 
167
171
  # Parse file as AST to find docstrings
@@ -206,8 +210,7 @@ def is_in_docstring_or_comment(line_content: str, file_content: str, line_num: i
206
210
 
207
211
 
208
212
  def enhanced_command_injection_detection(
209
- file_path: str,
210
- original_findings: list[dict[str, Any]]
213
+ file_path: str, original_findings: list[dict[str, Any]]
211
214
  ) -> list[dict[str, Any]]:
212
215
  """Enhanced command injection detection with AST-based filtering.
213
216
 
@@ -248,21 +251,24 @@ def enhanced_command_injection_detection(
248
251
 
249
252
  # Check if this is a test file (downgrade severity)
250
253
  from .security_audit import TEST_FILE_PATTERNS
254
+
251
255
  is_test_file = any(re.search(pat, file_path) for pat in TEST_FILE_PATTERNS)
252
256
 
253
257
  # Convert AST findings to format compatible with original
254
258
  filtered = []
255
259
  for finding in ast_findings:
256
- filtered.append({
257
- "type": "command_injection",
258
- "file": file_path,
259
- "line": finding["line"],
260
- "match": f"{finding['function']}(",
261
- "severity": "low" if is_test_file else "critical",
262
- "owasp": "A03:2021 Injection",
263
- "context": finding.get("context", ""),
264
- "is_test": is_test_file,
265
- })
260
+ filtered.append(
261
+ {
262
+ "type": "command_injection",
263
+ "file": file_path,
264
+ "line": finding["line"],
265
+ "match": f"{finding['function']}(",
266
+ "severity": "low" if is_test_file else "critical",
267
+ "owasp": "A03:2021 Injection",
268
+ "context": finding.get("context", ""),
269
+ "is_test": is_test_file,
270
+ }
271
+ )
266
272
 
267
273
  # Keep subprocess/os.system findings (not filtered by AST)
268
274
  filtered.extend(subprocess_findings)
@@ -244,29 +244,35 @@ class SEOOptimizationWorkflow(BaseWorkflow):
244
244
 
245
245
  # Check for missing meta description
246
246
  if "description:" not in content and "# " in content:
247
- issues.append({
248
- "file": file_path,
249
- "element": "meta_description",
250
- "severity": "critical",
251
- "message": "Missing meta description"
252
- })
247
+ issues.append(
248
+ {
249
+ "file": file_path,
250
+ "element": "meta_description",
251
+ "severity": "critical",
252
+ "message": "Missing meta description",
253
+ }
254
+ )
253
255
 
254
256
  # Check for H1 count
255
257
  h1_count = content.count("# ")
256
258
  if h1_count == 0:
257
- issues.append({
258
- "file": file_path,
259
- "element": "h1_count",
260
- "severity": "warning",
261
- "message": "No H1 heading found"
262
- })
259
+ issues.append(
260
+ {
261
+ "file": file_path,
262
+ "element": "h1_count",
263
+ "severity": "warning",
264
+ "message": "No H1 heading found",
265
+ }
266
+ )
263
267
  elif h1_count > 1:
264
- issues.append({
265
- "file": file_path,
266
- "element": "h1_count",
267
- "severity": "warning",
268
- "message": f"Multiple H1 headings ({h1_count})"
269
- })
268
+ issues.append(
269
+ {
270
+ "file": file_path,
271
+ "element": "h1_count",
272
+ "severity": "warning",
273
+ "message": f"Multiple H1 headings ({h1_count})",
274
+ }
275
+ )
270
276
  except Exception:
271
277
  pass # Skip files that can't be read
272
278
 
@@ -310,15 +316,11 @@ class SEOOptimizationWorkflow(BaseWorkflow):
310
316
  recommendations.append(recommendation)
311
317
  else:
312
318
  # Low confidence - flag for user clarification
313
- recommendation["question"] = self._generate_clarifying_question(
314
- issue, confidence
315
- )
319
+ recommendation["question"] = self._generate_clarifying_question(issue, confidence)
316
320
  needs_clarification.append(recommendation)
317
321
 
318
322
  # Sort recommendations by priority and confidence
319
- recommendations.sort(
320
- key=lambda r: (r["priority"] == "high", r["confidence"]), reverse=True
321
- )
323
+ recommendations.sort(key=lambda r: (r["priority"] == "high", r["confidence"]), reverse=True)
322
324
 
323
325
  return {
324
326
  "total_recommendations": len(recommendations),
@@ -328,9 +330,7 @@ class SEOOptimizationWorkflow(BaseWorkflow):
328
330
  "clarification_needed": needs_clarification,
329
331
  }
330
332
 
331
- def _generate_clarifying_question(
332
- self, issue: dict[str, Any], confidence: float
333
- ) -> str:
333
+ def _generate_clarifying_question(self, issue: dict[str, Any], confidence: float) -> str:
334
334
  """Generate clarifying question for low-confidence issues.
335
335
 
336
336
  Args:
@@ -7,7 +7,6 @@ Licensed under Fair Source License 0.9
7
7
  """
8
8
 
9
9
 
10
-
11
10
  def generate_test_for_function(module: str, func: dict) -> str:
12
11
  """Generate executable tests for a function based on AST analysis."""
13
12
  name = func["name"]
@@ -215,9 +214,7 @@ def generate_test_cases_for_params(params: list) -> dict:
215
214
  return {
216
215
  "valid_args": valid_args,
217
216
  "parametrize_cases": parametrize_cases[:5], # Limit cases
218
- "edge_cases": list(dict.fromkeys(edge_cases))[
219
- :5
220
- ], # Unique edge cases (preserves order)
217
+ "edge_cases": list(dict.fromkeys(edge_cases))[:5], # Unique edge cases (preserves order)
221
218
  }
222
219
 
223
220
 
@@ -631,8 +631,6 @@ class TestGenerationWorkflow(BaseWorkflow):
631
631
  )
632
632
 
633
633
 
634
-
635
-
636
634
  def main():
637
635
  """CLI entry point for test generation workflow."""
638
636
  import asyncio
@@ -122,20 +122,13 @@ class BehavioralTestLLMGenerator(LLMWorkflowGenerator):
122
122
  Formatted prompt for LLM
123
123
  """
124
124
  module_name = Path(module_info.file_path).stem
125
- import_path = (
126
- module_info.file_path.replace("src/", "").replace(".py", "").replace("/", ".")
127
- )
125
+ import_path = module_info.file_path.replace("src/", "").replace(".py", "").replace("/", ".")
128
126
 
129
127
  # Build context about what to test
130
128
  classes_info = "\n".join(
131
- [
132
- f" - {cls['name']}: {len(cls['methods'])} methods"
133
- for cls in module_info.classes
134
- ]
135
- )
136
- functions_info = "\n".join(
137
- [f" - {func['name']}()" for func in module_info.functions[:10]]
129
+ [f" - {cls['name']}: {len(cls['methods'])} methods" for cls in module_info.classes]
138
130
  )
131
+ functions_info = "\n".join([f" - {func['name']}()" for func in module_info.functions[:10]])
139
132
 
140
133
  prompt = f"""Generate comprehensive behavioral tests for this Python module.
141
134
 
@@ -210,9 +203,7 @@ Return ONLY the complete Python test file content, no explanations."""
210
203
  Template test file content
211
204
  """
212
205
  module_name = Path(module_info.file_path).stem
213
- import_path = (
214
- module_info.file_path.replace("src/", "").replace(".py", "").replace("/", ".")
215
- )
206
+ import_path = module_info.file_path.replace("src/", "").replace(".py", "").replace("/", ".")
216
207
 
217
208
  lines = [
218
209
  f'"""Behavioral tests for {module_name}.py - GENERATED BY EMPATHY FRAMEWORK.',
@@ -371,9 +362,7 @@ class BehavioralTestGenerationWorkflow(BaseWorkflow):
371
362
  ),
372
363
  },
373
364
  )
374
- self.llm_generator = BehavioralTestLLMGenerator(
375
- model_tier=model_tier, use_llm=use_llm
376
- )
365
+ self.llm_generator = BehavioralTestLLMGenerator(model_tier=model_tier, use_llm=use_llm)
377
366
 
378
367
  def analyze_module(self, file_path: Path) -> ModuleInfo:
379
368
  """Analyze a Python module."""
@@ -451,9 +440,7 @@ class BehavioralTestGenerationWorkflow(BaseWorkflow):
451
440
  test_path = output_path / test_filename
452
441
 
453
442
  # Generate comprehensive tests with LLM
454
- template = self.generate_test_template(
455
- module_info, test_path, source_code=source_code
456
- )
443
+ template = self.generate_test_template(module_info, test_path, source_code=source_code)
457
444
  test_path.write_text(template)
458
445
 
459
446
  generated_files.append(str(test_path))
@@ -93,12 +93,12 @@ Generate complete, runnable tests that will increase coverage.""",
93
93
  import subprocess
94
94
 
95
95
  subprocess.run(
96
- ["coverage", "json", "-o", "/tmp/coverage_batch.json"],
96
+ ["coverage", "json", "-o", "/tmp/coverage_batch.json"], # nosec B108
97
97
  capture_output=True,
98
98
  check=True,
99
99
  )
100
100
 
101
- with open("/tmp/coverage_batch.json") as f:
101
+ with open("/tmp/coverage_batch.json") as f: # nosec B108
102
102
  data = json.load(f)
103
103
 
104
104
  coverage_by_file = []
@@ -136,9 +136,7 @@ Generate complete, runnable tests that will increase coverage.""",
136
136
  for n in node.body
137
137
  if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))
138
138
  ]
139
- classes.append(
140
- {"name": node.name, "methods": methods, "line": node.lineno}
141
- )
139
+ classes.append({"name": node.name, "methods": methods, "line": node.lineno})
142
140
  elif isinstance(node, ast.FunctionDef) and node.col_offset == 0:
143
141
  functions.append({"name": node.name, "line": node.lineno})
144
142
 
@@ -278,7 +276,11 @@ Output the COMPLETE test file, no TODOs remaining."""
278
276
  return content
279
277
 
280
278
  async def execute(
281
- self, top: int = 200, batch_size: int = 10, output_dir: str = "tests/behavioral/generated", **kwargs
279
+ self,
280
+ top: int = 200,
281
+ batch_size: int = 10,
282
+ output_dir: str = "tests/behavioral/generated",
283
+ **kwargs,
282
284
  ) -> WorkflowResult:
283
285
  """Execute parallel test generation workflow.
284
286
 
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: attune-ai
3
- Version: 2.1.5
4
- Summary: AI collaboration framework with real LLM agent execution, AskUserQuestion tool integration, Socratic agent generation, progressive tier escalation (70-85% cost savings), meta-orchestration, dynamic agent composition (10 patterns including Anthropic-inspired), intelligent caching (85% hit rate), semantic workflow discovery, visual workflow editor, MCP integration for Claude Code, and multi-agent orchestration.
3
+ Version: 2.2.1
4
+ Summary: AI-powered developer workflows for Claude with cost optimization, multi-agent orchestration, and workflow automation.
5
5
  Author-email: Patrick Roebuck <admin@smartaimemory.com>
6
6
  Maintainer-email: Smart-AI-Memory <admin@smartaimemory.com>
7
7
  License: Apache License
@@ -240,6 +240,7 @@ Requires-Dist: rich<14.0.0,>=13.0.0
240
240
  Requires-Dist: typer<1.0.0,>=0.9.0
241
241
  Requires-Dist: pyyaml<7.0,>=6.0
242
242
  Requires-Dist: anthropic<1.0.0,>=0.25.0
243
+ Requires-Dist: redis<6.0.0,>=4.0.0
243
244
  Provides-Extra: anthropic
244
245
  Requires-Dist: anthropic<1.0.0,>=0.25.0; extra == "anthropic"
245
246
  Provides-Extra: openai
@@ -428,7 +429,7 @@ Dynamic: license-file
428
429
  Run code review, debugging, testing, and release workflows from your terminal or Claude Code. Smart tier routing saves 34-86% on LLM costs.
429
430
 
430
431
  [![PyPI](https://img.shields.io/pypi/v/attune-ai?color=blue)](https://pypi.org/project/attune-ai/)
431
- [![Tests](https://img.shields.io/badge/tests-7%2C168%20passing%20(99.9%25)-brightgreen)](https://github.com/Smart-AI-Memory/attune-ai/actions)
432
+ [![Tests](https://img.shields.io/badge/tests-passing-brightgreen)](https://github.com/Smart-AI-Memory/attune-ai/actions/workflows/tests.yml)
432
433
  [![Python](https://img.shields.io/badge/python-3.10+-blue)](https://www.python.org)
433
434
  [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)
434
435