attune-ai 2.1.4__py3-none-any.whl → 2.2.0__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.
- attune/cli/__init__.py +3 -55
- attune/cli/commands/batch.py +4 -12
- attune/cli/commands/cache.py +7 -15
- attune/cli/commands/provider.py +17 -0
- attune/cli/commands/routing.py +3 -1
- attune/cli/commands/setup.py +122 -0
- attune/cli/commands/tier.py +1 -3
- attune/cli/commands/workflow.py +31 -0
- attune/cli/parsers/cache.py +1 -0
- attune/cli/parsers/help.py +1 -3
- attune/cli/parsers/provider.py +7 -0
- attune/cli/parsers/routing.py +1 -3
- attune/cli/parsers/setup.py +7 -0
- attune/cli/parsers/status.py +1 -3
- attune/cli/parsers/tier.py +1 -3
- attune/cli_minimal.py +34 -28
- attune/cli_router.py +9 -7
- attune/cli_unified.py +3 -0
- attune/core.py +190 -0
- attune/dashboard/app.py +4 -2
- attune/dashboard/simple_server.py +3 -1
- attune/dashboard/standalone_server.py +7 -3
- attune/mcp/server.py +54 -102
- attune/memory/long_term.py +0 -2
- attune/memory/short_term/__init__.py +84 -0
- attune/memory/short_term/base.py +467 -0
- attune/memory/short_term/batch.py +219 -0
- attune/memory/short_term/caching.py +227 -0
- attune/memory/short_term/conflicts.py +265 -0
- attune/memory/short_term/cross_session.py +122 -0
- attune/memory/short_term/facade.py +655 -0
- attune/memory/short_term/pagination.py +215 -0
- attune/memory/short_term/patterns.py +271 -0
- attune/memory/short_term/pubsub.py +286 -0
- attune/memory/short_term/queues.py +244 -0
- attune/memory/short_term/security.py +300 -0
- attune/memory/short_term/sessions.py +250 -0
- attune/memory/short_term/streams.py +249 -0
- attune/memory/short_term/timelines.py +234 -0
- attune/memory/short_term/transactions.py +186 -0
- attune/memory/short_term/working.py +252 -0
- attune/meta_workflows/cli_commands/__init__.py +3 -0
- attune/meta_workflows/cli_commands/agent_commands.py +0 -4
- attune/meta_workflows/cli_commands/analytics_commands.py +0 -6
- attune/meta_workflows/cli_commands/config_commands.py +0 -5
- attune/meta_workflows/cli_commands/memory_commands.py +0 -5
- attune/meta_workflows/cli_commands/template_commands.py +0 -5
- attune/meta_workflows/cli_commands/workflow_commands.py +0 -6
- attune/meta_workflows/workflow.py +1 -1
- attune/models/adaptive_routing.py +4 -8
- attune/models/auth_cli.py +3 -9
- attune/models/auth_strategy.py +2 -4
- attune/models/provider_config.py +20 -1
- attune/models/telemetry/analytics.py +0 -2
- attune/models/telemetry/backend.py +0 -3
- attune/models/telemetry/storage.py +0 -2
- attune/orchestration/_strategies/__init__.py +156 -0
- attune/orchestration/_strategies/base.py +231 -0
- attune/orchestration/_strategies/conditional_strategies.py +373 -0
- attune/orchestration/_strategies/conditions.py +369 -0
- attune/orchestration/_strategies/core_strategies.py +491 -0
- attune/orchestration/_strategies/data_classes.py +64 -0
- attune/orchestration/_strategies/nesting.py +233 -0
- attune/orchestration/execution_strategies.py +58 -1567
- attune/orchestration/meta_orchestrator.py +1 -3
- attune/project_index/scanner.py +1 -3
- attune/project_index/scanner_parallel.py +7 -5
- attune/socratic_router.py +1 -3
- attune/telemetry/agent_coordination.py +9 -3
- attune/telemetry/agent_tracking.py +16 -3
- attune/telemetry/approval_gates.py +22 -5
- attune/telemetry/cli.py +3 -3
- attune/telemetry/commands/dashboard_commands.py +24 -8
- attune/telemetry/event_streaming.py +8 -2
- attune/telemetry/feedback_loop.py +10 -2
- attune/tools.py +1 -0
- attune/workflow_commands.py +1 -3
- attune/workflows/__init__.py +53 -10
- attune/workflows/autonomous_test_gen.py +160 -104
- attune/workflows/base.py +48 -664
- attune/workflows/batch_processing.py +2 -4
- attune/workflows/compat.py +156 -0
- attune/workflows/cost_mixin.py +141 -0
- attune/workflows/data_classes.py +92 -0
- attune/workflows/document_gen/workflow.py +11 -14
- attune/workflows/history.py +62 -37
- attune/workflows/llm_base.py +2 -4
- attune/workflows/migration.py +422 -0
- attune/workflows/output.py +3 -9
- attune/workflows/parsing_mixin.py +427 -0
- attune/workflows/perf_audit.py +3 -1
- attune/workflows/progress.py +10 -13
- attune/workflows/release_prep.py +5 -1
- attune/workflows/routing.py +0 -2
- attune/workflows/secure_release.py +2 -1
- attune/workflows/security_audit.py +19 -14
- attune/workflows/security_audit_phase3.py +28 -22
- attune/workflows/seo_optimization.py +29 -29
- attune/workflows/test_gen/test_templates.py +1 -4
- attune/workflows/test_gen/workflow.py +0 -2
- attune/workflows/test_gen_behavioral.py +7 -20
- attune/workflows/test_gen_parallel.py +6 -4
- {attune_ai-2.1.4.dist-info → attune_ai-2.2.0.dist-info}/METADATA +4 -3
- {attune_ai-2.1.4.dist-info → attune_ai-2.2.0.dist-info}/RECORD +119 -94
- {attune_ai-2.1.4.dist-info → attune_ai-2.2.0.dist-info}/entry_points.txt +0 -2
- attune_healthcare/monitors/monitoring/__init__.py +9 -9
- attune_llm/agent_factory/__init__.py +6 -6
- attune_llm/commands/__init__.py +10 -10
- attune_llm/commands/models.py +3 -3
- attune_llm/config/__init__.py +8 -8
- attune_llm/learning/__init__.py +3 -3
- attune_llm/learning/extractor.py +5 -3
- attune_llm/learning/storage.py +5 -3
- attune_llm/security/__init__.py +17 -17
- attune_llm/utils/tokens.py +3 -1
- attune/cli_legacy.py +0 -3957
- attune/memory/short_term.py +0 -2192
- attune/workflows/manage_docs.py +0 -87
- attune/workflows/test5.py +0 -125
- {attune_ai-2.1.4.dist-info → attune_ai-2.2.0.dist-info}/WHEEL +0 -0
- {attune_ai-2.1.4.dist-info → attune_ai-2.2.0.dist-info}/licenses/LICENSE +0 -0
- {attune_ai-2.1.4.dist-info → attune_ai-2.2.0.dist-info}/licenses/LICENSE_CHANGE_ANNOUNCEMENT.md +0 -0
- {attune_ai-2.1.4.dist-info → attune_ai-2.2.0.dist-info}/top_level.txt +0 -0
attune/cli/__init__.py
CHANGED
|
@@ -60,21 +60,9 @@ def main() -> int:
|
|
|
60
60
|
|
|
61
61
|
register_all_parsers(subparsers)
|
|
62
62
|
|
|
63
|
-
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
# NOTE: Temporarily disabled to avoid conflicts with extracted commands.
|
|
68
|
-
# Commands that have been extracted:
|
|
69
|
-
# - help, tier, info, patterns, status (Phase 1)
|
|
70
|
-
# - workflow, inspect (run, inspect, export, import) (Phase 2)
|
|
71
|
-
# Once all commands are extracted, the old cli.py will be removed entirely.
|
|
72
|
-
#
|
|
73
|
-
# try:
|
|
74
|
-
# from attune import cli as old_cli
|
|
75
|
-
# _register_legacy_commands(subparsers, old_cli)
|
|
76
|
-
# except ImportError:
|
|
77
|
-
# pass # Old cli.py not available or already moved
|
|
63
|
+
# NOTE: CLI refactoring is COMPLETE (v2.1.5)
|
|
64
|
+
# All 30 commands have been extracted from the monolithic cli_legacy.py
|
|
65
|
+
# to the new modular structure in cli/commands/ and cli/parsers/.
|
|
78
66
|
|
|
79
67
|
# Parse arguments
|
|
80
68
|
args = parser.parse_args()
|
|
@@ -107,46 +95,6 @@ def main() -> int:
|
|
|
107
95
|
return 0
|
|
108
96
|
|
|
109
97
|
|
|
110
|
-
def _register_legacy_commands(subparsers, old_cli):
|
|
111
|
-
"""Temporarily register commands not yet extracted from old cli.py.
|
|
112
|
-
|
|
113
|
-
This function provides backward compatibility during the refactoring process.
|
|
114
|
-
As commands are extracted into the new structure, they should be removed
|
|
115
|
-
from this registration.
|
|
116
|
-
|
|
117
|
-
Args:
|
|
118
|
-
subparsers: ArgumentParser subparsers object
|
|
119
|
-
old_cli: Reference to old cli module
|
|
120
|
-
|
|
121
|
-
Note:
|
|
122
|
-
This is a TEMPORARY function that will be removed once all commands
|
|
123
|
-
are extracted from the monolithic cli.py file.
|
|
124
|
-
"""
|
|
125
|
-
# Import command functions that haven't been extracted yet
|
|
126
|
-
try:
|
|
127
|
-
# Patterns commands
|
|
128
|
-
from attune.cli import cmd_patterns_export, cmd_patterns_list, cmd_patterns_resolve
|
|
129
|
-
|
|
130
|
-
patterns_parser = subparsers.add_parser("patterns", help="Pattern management")
|
|
131
|
-
patterns_sub = patterns_parser.add_subparsers(dest="patterns_command")
|
|
132
|
-
|
|
133
|
-
p_list = patterns_sub.add_parser("list", help="List patterns")
|
|
134
|
-
p_list.set_defaults(func=cmd_patterns_list)
|
|
135
|
-
|
|
136
|
-
p_export = patterns_sub.add_parser("export", help="Export patterns")
|
|
137
|
-
p_export.add_argument("output", help="Output file")
|
|
138
|
-
p_export.set_defaults(func=cmd_patterns_export)
|
|
139
|
-
|
|
140
|
-
p_resolve = patterns_sub.add_parser("resolve", help="Resolve pattern")
|
|
141
|
-
p_resolve.add_argument("pattern_id", help="Pattern ID")
|
|
142
|
-
p_resolve.set_defaults(func=cmd_patterns_resolve)
|
|
143
|
-
except (ImportError, AttributeError):
|
|
144
|
-
pass # Commands not available
|
|
145
|
-
|
|
146
|
-
# Add other legacy commands as needed...
|
|
147
|
-
# This list will shrink as commands are extracted
|
|
148
|
-
|
|
149
|
-
|
|
150
98
|
# Preserve backward compatibility
|
|
151
99
|
if __name__ == "__main__":
|
|
152
100
|
sys.exit(main())
|
attune/cli/commands/batch.py
CHANGED
|
@@ -184,12 +184,8 @@ def cmd_batch_results(args):
|
|
|
184
184
|
print(f" Total: {len(results)} results")
|
|
185
185
|
|
|
186
186
|
# Summary
|
|
187
|
-
succeeded = sum(
|
|
188
|
-
|
|
189
|
-
)
|
|
190
|
-
errored = sum(
|
|
191
|
-
1 for r in results if r.get("result", {}).get("type") == "errored"
|
|
192
|
-
)
|
|
187
|
+
succeeded = sum(1 for r in results if r.get("result", {}).get("type") == "succeeded")
|
|
188
|
+
errored = sum(1 for r in results if r.get("result", {}).get("type") == "errored")
|
|
193
189
|
|
|
194
190
|
print(f" Succeeded: {succeeded}")
|
|
195
191
|
print(f" Errored: {errored}")
|
|
@@ -242,12 +238,8 @@ def cmd_batch_wait(args):
|
|
|
242
238
|
print(f" Total: {len(results)} results")
|
|
243
239
|
|
|
244
240
|
# Summary
|
|
245
|
-
succeeded = sum(
|
|
246
|
-
|
|
247
|
-
)
|
|
248
|
-
errored = sum(
|
|
249
|
-
1 for r in results if r.get("result", {}).get("type") == "errored"
|
|
250
|
-
)
|
|
241
|
+
succeeded = sum(1 for r in results if r.get("result", {}).get("type") == "succeeded")
|
|
242
|
+
errored = sum(1 for r in results if r.get("result", {}).get("type") == "errored")
|
|
251
243
|
|
|
252
244
|
print(f" Succeeded: {succeeded}")
|
|
253
245
|
print(f" Errored: {errored}")
|
attune/cli/commands/cache.py
CHANGED
|
@@ -93,14 +93,10 @@ def _collect_cache_stats(days: int = 7) -> dict[str, Any]:
|
|
|
93
93
|
for line in f:
|
|
94
94
|
# Try to extract timestamp
|
|
95
95
|
# Common format: 2026-01-27 21:30:45,123
|
|
96
|
-
timestamp_match = re.match(
|
|
97
|
-
r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})", line
|
|
98
|
-
)
|
|
96
|
+
timestamp_match = re.match(r"(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})", line)
|
|
99
97
|
if timestamp_match:
|
|
100
98
|
try:
|
|
101
|
-
log_time = datetime.strptime(
|
|
102
|
-
timestamp_match.group(1), "%Y-%m-%d %H:%M:%S"
|
|
103
|
-
)
|
|
99
|
+
log_time = datetime.strptime(timestamp_match.group(1), "%Y-%m-%d %H:%M:%S")
|
|
104
100
|
if log_time < cutoff_date:
|
|
105
101
|
continue # Skip old entries
|
|
106
102
|
except ValueError:
|
|
@@ -140,9 +136,7 @@ def _collect_cache_stats(days: int = 7) -> dict[str, Any]:
|
|
|
140
136
|
}
|
|
141
137
|
|
|
142
138
|
# Calculate metrics
|
|
143
|
-
cache_hit_rate = (
|
|
144
|
-
(cache_hits / total_requests * 100) if total_requests > 0 else 0.0
|
|
145
|
-
)
|
|
139
|
+
cache_hit_rate = (cache_hits / total_requests * 100) if total_requests > 0 else 0.0
|
|
146
140
|
|
|
147
141
|
return {
|
|
148
142
|
"days_analyzed": days,
|
|
@@ -154,9 +148,7 @@ def _collect_cache_stats(days: int = 7) -> dict[str, Any]:
|
|
|
154
148
|
"total_cache_read_tokens": total_cache_read_tokens,
|
|
155
149
|
"total_cache_write_tokens": total_cache_write_tokens,
|
|
156
150
|
"total_savings": round(total_savings, 4),
|
|
157
|
-
"avg_savings_per_hit": (
|
|
158
|
-
round(total_savings / cache_hits, 4) if cache_hits > 0 else 0.0
|
|
159
|
-
),
|
|
151
|
+
"avg_savings_per_hit": (round(total_savings / cache_hits, 4) if cache_hits > 0 else 0.0),
|
|
160
152
|
}
|
|
161
153
|
|
|
162
154
|
|
|
@@ -193,7 +185,7 @@ def _display_cache_report(stats: dict[str, Any], verbose: bool = False):
|
|
|
193
185
|
# Cost savings
|
|
194
186
|
print("💰 Cost Savings:")
|
|
195
187
|
print(f" Total Saved: ${stats['total_savings']:.4f}")
|
|
196
|
-
if stats[
|
|
188
|
+
if stats["cache_hits"] > 0:
|
|
197
189
|
print(f" Avg Savings per Hit: ${stats['avg_savings_per_hit']:.4f}")
|
|
198
190
|
print()
|
|
199
191
|
|
|
@@ -205,7 +197,7 @@ def _display_cache_report(stats: dict[str, Any], verbose: bool = False):
|
|
|
205
197
|
print()
|
|
206
198
|
|
|
207
199
|
# Performance assessment
|
|
208
|
-
hit_rate = stats[
|
|
200
|
+
hit_rate = stats["cache_hit_rate"]
|
|
209
201
|
print("📈 Performance Assessment:")
|
|
210
202
|
if hit_rate >= 50:
|
|
211
203
|
print(" ✅ EXCELLENT - Cache is working effectively")
|
|
@@ -222,7 +214,7 @@ def _display_cache_report(stats: dict[str, Any], verbose: bool = False):
|
|
|
222
214
|
print()
|
|
223
215
|
|
|
224
216
|
# Recommendations
|
|
225
|
-
if stats[
|
|
217
|
+
if stats["total_requests"] < 10:
|
|
226
218
|
print("ℹ️ Note: Limited data available. Run more workflows for accurate stats.")
|
|
227
219
|
elif hit_rate < 30:
|
|
228
220
|
print("💡 Recommendations:")
|
attune/cli/commands/provider.py
CHANGED
|
@@ -96,3 +96,20 @@ def cmd_provider_set(args):
|
|
|
96
96
|
|
|
97
97
|
print(f"✓ Default provider set to: {provider}")
|
|
98
98
|
print(f" Saved to: {validated_workflows_path}")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def cmd_provider_hybrid(args):
|
|
102
|
+
"""Configure hybrid mode (DEPRECATED in v5.0.0).
|
|
103
|
+
|
|
104
|
+
Hybrid mode is no longer supported. This command now configures
|
|
105
|
+
Anthropic as the sole provider.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
args: Namespace object from argparse (no additional attributes used).
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
None: Launches interactive Anthropic configuration.
|
|
112
|
+
"""
|
|
113
|
+
from attune.models.provider_config import configure_hybrid_interactive
|
|
114
|
+
|
|
115
|
+
configure_hybrid_interactive()
|
attune/cli/commands/routing.py
CHANGED
|
@@ -82,7 +82,9 @@ def cmd_routing_stats(args: Any) -> int:
|
|
|
82
82
|
)
|
|
83
83
|
|
|
84
84
|
print(f" Best model: {best_model[0]}")
|
|
85
|
-
print(
|
|
85
|
+
print(
|
|
86
|
+
f" ({best_model[1]['success_rate']:.1%} success, ${best_model[1]['avg_cost']:.4f}/call)"
|
|
87
|
+
)
|
|
86
88
|
|
|
87
89
|
# Cost savings potential
|
|
88
90
|
if len(stats["models_used"]) > 1:
|
attune/cli/commands/setup.py
CHANGED
|
@@ -94,3 +94,125 @@ def cmd_validate(args):
|
|
|
94
94
|
logger.exception(f"Unexpected error validating configuration: {e}")
|
|
95
95
|
logger.error(f"✗ Configuration invalid: {e}")
|
|
96
96
|
sys.exit(1)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def cmd_setup(args):
|
|
100
|
+
"""Interactive setup workflow.
|
|
101
|
+
|
|
102
|
+
Guides user through initial framework configuration step by step.
|
|
103
|
+
Updated for Claude-native v5.0.0 (Anthropic-only).
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
args: Namespace object from argparse (no additional attributes used).
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
None: Creates attune.config.yml with user's choices.
|
|
110
|
+
"""
|
|
111
|
+
print("🧙 Attune AI Setup Workflow")
|
|
112
|
+
print("=" * 50)
|
|
113
|
+
print("\nI'll help you set up your Attune AI configuration.\n")
|
|
114
|
+
|
|
115
|
+
# Step 1: Use case
|
|
116
|
+
print("1. What's your primary use case?")
|
|
117
|
+
print(" [1] Software development")
|
|
118
|
+
print(" [2] Healthcare applications")
|
|
119
|
+
print(" [3] Customer support")
|
|
120
|
+
print(" [4] Other")
|
|
121
|
+
|
|
122
|
+
use_case_choice = input("\nYour choice (1-4): ").strip()
|
|
123
|
+
use_case_map = {
|
|
124
|
+
"1": "software_development",
|
|
125
|
+
"2": "healthcare",
|
|
126
|
+
"3": "customer_support",
|
|
127
|
+
"4": "general",
|
|
128
|
+
}
|
|
129
|
+
use_case = use_case_map.get(use_case_choice, "general")
|
|
130
|
+
|
|
131
|
+
# Step 2: Empathy level
|
|
132
|
+
print("\n2. What empathy level do you want to target?")
|
|
133
|
+
print(" [1] Level 1 - Reactive (basic Q&A)")
|
|
134
|
+
print(" [2] Level 2 - Guided (asks clarifying questions)")
|
|
135
|
+
print(" [3] Level 3 - Proactive (offers improvements)")
|
|
136
|
+
print(" [4] Level 4 - Anticipatory (predicts problems) ⭐ Recommended")
|
|
137
|
+
print(" [5] Level 5 - Transformative (reshapes workflows)")
|
|
138
|
+
|
|
139
|
+
level_choice = input("\nYour choice (1-5) [4]: ").strip() or "4"
|
|
140
|
+
target_level = int(level_choice) if level_choice in ["1", "2", "3", "4", "5"] else 4
|
|
141
|
+
|
|
142
|
+
# Step 3: LLM provider (Anthropic-only as of v5.0.0)
|
|
143
|
+
print("\n3. LLM Provider: Anthropic Claude")
|
|
144
|
+
print(" Attune AI is Claude-native and uses Anthropic exclusively.")
|
|
145
|
+
|
|
146
|
+
from attune.models.provider_config import ProviderConfig
|
|
147
|
+
|
|
148
|
+
config_detected = ProviderConfig.auto_detect()
|
|
149
|
+
if config_detected.available_providers:
|
|
150
|
+
print(" ✓ ANTHROPIC_API_KEY detected")
|
|
151
|
+
else:
|
|
152
|
+
print(" ⚠️ ANTHROPIC_API_KEY not detected")
|
|
153
|
+
print(" Set your API key: export ANTHROPIC_API_KEY='your-key-here'")
|
|
154
|
+
print(" Get key at: https://console.anthropic.com/settings/keys")
|
|
155
|
+
|
|
156
|
+
# Step 4: User ID
|
|
157
|
+
print("\n4. What user ID should we use?")
|
|
158
|
+
user_id = input("User ID [default_user]: ").strip() or "default_user"
|
|
159
|
+
|
|
160
|
+
# Generate configuration
|
|
161
|
+
config = {
|
|
162
|
+
"user_id": user_id,
|
|
163
|
+
"target_level": target_level,
|
|
164
|
+
"confidence_threshold": 0.75,
|
|
165
|
+
"persistence_enabled": True,
|
|
166
|
+
"persistence_backend": "sqlite",
|
|
167
|
+
"persistence_path": ".attune",
|
|
168
|
+
"metrics_enabled": True,
|
|
169
|
+
"use_case": use_case,
|
|
170
|
+
"llm_provider": "anthropic",
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# Save configuration
|
|
174
|
+
output_file = "attune.config.yml"
|
|
175
|
+
print(f"\n5. Creating configuration file: {output_file}")
|
|
176
|
+
|
|
177
|
+
# Write YAML config
|
|
178
|
+
yaml_content = f"""# Attune AI Configuration
|
|
179
|
+
# Generated by setup workflow
|
|
180
|
+
|
|
181
|
+
# Core settings
|
|
182
|
+
user_id: "{config["user_id"]}"
|
|
183
|
+
target_level: {config["target_level"]}
|
|
184
|
+
confidence_threshold: {config["confidence_threshold"]}
|
|
185
|
+
|
|
186
|
+
# Use case
|
|
187
|
+
use_case: "{config["use_case"]}"
|
|
188
|
+
|
|
189
|
+
# Persistence
|
|
190
|
+
persistence_enabled: {str(config["persistence_enabled"]).lower()}
|
|
191
|
+
persistence_backend: "{config["persistence_backend"]}"
|
|
192
|
+
persistence_path: "{config["persistence_path"]}"
|
|
193
|
+
|
|
194
|
+
# Metrics
|
|
195
|
+
metrics_enabled: {str(config["metrics_enabled"]).lower()}
|
|
196
|
+
|
|
197
|
+
# LLM Provider (Claude-native v5.0.0)
|
|
198
|
+
llm_provider: "anthropic"
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
validated_output = _validate_file_path(output_file)
|
|
202
|
+
with open(validated_output, "w") as f:
|
|
203
|
+
f.write(yaml_content)
|
|
204
|
+
|
|
205
|
+
print(f" ✓ Created {validated_output}")
|
|
206
|
+
|
|
207
|
+
print("\n" + "=" * 50)
|
|
208
|
+
print("✅ Setup complete!")
|
|
209
|
+
print("\nNext steps:")
|
|
210
|
+
print(f" 1. Edit {output_file} to customize settings")
|
|
211
|
+
|
|
212
|
+
if not config_detected.available_providers:
|
|
213
|
+
print(" 2. Set ANTHROPIC_API_KEY environment variable")
|
|
214
|
+
print(" 3. Run: attune run --config attune.config.yml")
|
|
215
|
+
else:
|
|
216
|
+
print(" 2. Run: attune run --config attune.config.yml")
|
|
217
|
+
|
|
218
|
+
print("\nHappy coding! 🧠✨\n")
|
attune/cli/commands/tier.py
CHANGED
|
@@ -109,9 +109,7 @@ def cmd_tier_stats(args):
|
|
|
109
109
|
|
|
110
110
|
print(" BUG TYPE DISTRIBUTION")
|
|
111
111
|
print(" " + "-" * 40)
|
|
112
|
-
sorted_types = sorted(
|
|
113
|
-
stats["bug_type_distribution"].items(), key=lambda x: x[1], reverse=True
|
|
114
|
-
)
|
|
112
|
+
sorted_types = sorted(stats["bug_type_distribution"].items(), key=lambda x: x[1], reverse=True)
|
|
115
113
|
for bug_type, count in sorted_types[:10]:
|
|
116
114
|
percent = (count / stats["total_patterns"]) * 100
|
|
117
115
|
print(f" {bug_type:20} {count:3} ({percent:5.1f}%)")
|
attune/cli/commands/workflow.py
CHANGED
|
@@ -14,6 +14,11 @@ from attune.logging_config import get_logger
|
|
|
14
14
|
from attune.workflows import get_workflow
|
|
15
15
|
from attune.workflows import list_workflows as get_workflow_list
|
|
16
16
|
from attune.workflows.config import WorkflowConfig, create_example_config
|
|
17
|
+
from attune.workflows.migration import (
|
|
18
|
+
WORKFLOW_ALIASES,
|
|
19
|
+
resolve_workflow_migration,
|
|
20
|
+
show_migration_tip,
|
|
21
|
+
)
|
|
17
22
|
|
|
18
23
|
logger = get_logger(__name__)
|
|
19
24
|
|
|
@@ -127,6 +132,13 @@ def cmd_workflow(args):
|
|
|
127
132
|
return 1
|
|
128
133
|
|
|
129
134
|
try:
|
|
135
|
+
# Check for workflow migration (describe still works with old names)
|
|
136
|
+
if name in WORKFLOW_ALIASES:
|
|
137
|
+
resolved_name, migration_kwargs, _ = resolve_workflow_migration(name)
|
|
138
|
+
if resolved_name and resolved_name != name:
|
|
139
|
+
print(f"ℹ️ '{name}' is now '{resolved_name}'")
|
|
140
|
+
name = resolved_name
|
|
141
|
+
|
|
130
142
|
workflow_cls = get_workflow(name)
|
|
131
143
|
provider = getattr(args, "provider", None)
|
|
132
144
|
workflow = workflow_cls(provider=provider)
|
|
@@ -165,6 +177,16 @@ def cmd_workflow(args):
|
|
|
165
177
|
return 1
|
|
166
178
|
|
|
167
179
|
try:
|
|
180
|
+
# Check for workflow migration (environment-aware)
|
|
181
|
+
original_name = name
|
|
182
|
+
migration_kwargs = {}
|
|
183
|
+
was_migrated = False
|
|
184
|
+
|
|
185
|
+
if name in WORKFLOW_ALIASES:
|
|
186
|
+
name, migration_kwargs, was_migrated = resolve_workflow_migration(name)
|
|
187
|
+
if was_migrated:
|
|
188
|
+
logger.info(f"Migrated '{original_name}' -> '{name}' with {migration_kwargs}")
|
|
189
|
+
|
|
168
190
|
workflow_cls = get_workflow(name)
|
|
169
191
|
|
|
170
192
|
# Get provider from CLI arg, or fall back to config's default_provider
|
|
@@ -197,6 +219,11 @@ def cmd_workflow(args):
|
|
|
197
219
|
health_score_threshold = getattr(args, "health_score_threshold", 100)
|
|
198
220
|
workflow_kwargs["health_score_threshold"] = health_score_threshold
|
|
199
221
|
|
|
222
|
+
# Merge migration kwargs (from workflow consolidation)
|
|
223
|
+
for key, value in migration_kwargs.items():
|
|
224
|
+
if not key.startswith("_") and key in init_params:
|
|
225
|
+
workflow_kwargs[key] = value
|
|
226
|
+
|
|
200
227
|
workflow = workflow_cls(**workflow_kwargs)
|
|
201
228
|
|
|
202
229
|
# Parse input
|
|
@@ -367,6 +394,10 @@ def cmd_workflow(args):
|
|
|
367
394
|
error_msg = error_msg or "Unknown error"
|
|
368
395
|
print(f"\n✗ Workflow failed: {error_msg}\n")
|
|
369
396
|
|
|
397
|
+
# Show migration tip if workflow was migrated (helps users learn new syntax)
|
|
398
|
+
if was_migrated and original_name != name:
|
|
399
|
+
show_migration_tip(original_name, name, migration_kwargs)
|
|
400
|
+
|
|
370
401
|
except KeyError:
|
|
371
402
|
print(f"Error: Workflow '{name}' not found")
|
|
372
403
|
print("\nRun 'empathy workflow list' to see available workflows")
|
attune/cli/parsers/cache.py
CHANGED
attune/cli/parsers/help.py
CHANGED
|
@@ -20,9 +20,7 @@ def register_parsers(subparsers):
|
|
|
20
20
|
# cheatsheet command
|
|
21
21
|
parser_cheatsheet = subparsers.add_parser("cheatsheet", help="Quick reference guide")
|
|
22
22
|
parser_cheatsheet.add_argument("--category", help="Specific category to show")
|
|
23
|
-
parser_cheatsheet.add_argument(
|
|
24
|
-
"--compact", action="store_true", help="Show commands only"
|
|
25
|
-
)
|
|
23
|
+
parser_cheatsheet.add_argument("--compact", action="store_true", help="Show commands only")
|
|
26
24
|
parser_cheatsheet.set_defaults(func=help_commands.cmd_cheatsheet)
|
|
27
25
|
|
|
28
26
|
# onboard command
|
attune/cli/parsers/provider.py
CHANGED
|
@@ -38,3 +38,10 @@ def register_parsers(subparsers):
|
|
|
38
38
|
help="Provider name (anthropic only)",
|
|
39
39
|
)
|
|
40
40
|
p_set.set_defaults(func=provider.cmd_provider_set)
|
|
41
|
+
|
|
42
|
+
# Provider hybrid command (deprecated)
|
|
43
|
+
p_hybrid = provider_sub.add_parser(
|
|
44
|
+
"hybrid",
|
|
45
|
+
help="Configure hybrid mode (DEPRECATED - now Anthropic only)",
|
|
46
|
+
)
|
|
47
|
+
p_hybrid.set_defaults(func=provider.cmd_provider_hybrid)
|
attune/cli/parsers/routing.py
CHANGED
|
@@ -28,9 +28,7 @@ def register_parsers(subparsers):
|
|
|
28
28
|
)
|
|
29
29
|
|
|
30
30
|
# Routing subcommands
|
|
31
|
-
routing_subparsers = routing_parser.add_subparsers(
|
|
32
|
-
dest="routing_command", required=True
|
|
33
|
-
)
|
|
31
|
+
routing_subparsers = routing_parser.add_subparsers(dest="routing_command", required=True)
|
|
34
32
|
|
|
35
33
|
# routing stats command
|
|
36
34
|
stats_parser = routing_subparsers.add_parser(
|
attune/cli/parsers/setup.py
CHANGED
|
@@ -40,3 +40,10 @@ def register_parsers(subparsers):
|
|
|
40
40
|
help="Path to configuration file to validate",
|
|
41
41
|
)
|
|
42
42
|
parser_validate.set_defaults(func=setup.cmd_validate)
|
|
43
|
+
|
|
44
|
+
# Setup wizard command
|
|
45
|
+
parser_setup = subparsers.add_parser(
|
|
46
|
+
"setup",
|
|
47
|
+
help="Interactive setup workflow for first-time configuration",
|
|
48
|
+
)
|
|
49
|
+
parser_setup.set_defaults(func=setup.cmd_setup)
|
attune/cli/parsers/status.py
CHANGED
|
@@ -36,9 +36,7 @@ def register_parsers(subparsers):
|
|
|
36
36
|
parser_health.add_argument("--deep", action="store_true", help="Run all checks")
|
|
37
37
|
parser_health.add_argument("--fix", action="store_true", help="Auto-fix issues")
|
|
38
38
|
parser_health.add_argument("--dry-run", action="store_true", help="Preview fixes only")
|
|
39
|
-
parser_health.add_argument(
|
|
40
|
-
"--interactive", action="store_true", help="Interactive fix mode"
|
|
41
|
-
)
|
|
39
|
+
parser_health.add_argument("--interactive", action="store_true", help="Interactive fix mode")
|
|
42
40
|
parser_health.add_argument("--project-root", default=".", help="Project root")
|
|
43
41
|
parser_health.add_argument("--details", action="store_true", help="Show details")
|
|
44
42
|
parser_health.add_argument("--full", action="store_true", help="Show all details")
|
attune/cli/parsers/tier.py
CHANGED
|
@@ -18,9 +18,7 @@ def register_parsers(subparsers):
|
|
|
18
18
|
tier_subparsers = tier_parser.add_subparsers(dest="tier_command")
|
|
19
19
|
|
|
20
20
|
# tier recommend
|
|
21
|
-
parser_recommend = tier_subparsers.add_parser(
|
|
22
|
-
"recommend", help="Get tier recommendation"
|
|
23
|
-
)
|
|
21
|
+
parser_recommend = tier_subparsers.add_parser("recommend", help="Get tier recommendation")
|
|
24
22
|
parser_recommend.add_argument("description", help="Bug or task description")
|
|
25
23
|
parser_recommend.add_argument("--files", help="Comma-separated list of files")
|
|
26
24
|
parser_recommend.add_argument(
|
attune/cli_minimal.py
CHANGED
|
@@ -5,29 +5,29 @@ IMPORTANT: This CLI is for automation only (git hooks, scripts, CI/CD).
|
|
|
5
5
|
For interactive use, use Claude Code skills in VSCode or Claude Desktop.
|
|
6
6
|
|
|
7
7
|
Automation commands:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
attune workflow list List available workflows
|
|
9
|
+
attune workflow run <name> Execute a workflow
|
|
10
|
+
attune workflow info <name> Show workflow details
|
|
11
11
|
|
|
12
12
|
Monitoring commands:
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
attune dashboard start Start agent coordination dashboard
|
|
14
|
+
(opens web UI at http://localhost:8000)
|
|
15
15
|
|
|
16
16
|
Utility commands:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
attune telemetry show Display usage summary
|
|
18
|
+
attune telemetry savings Show cost savings
|
|
19
|
+
attune telemetry export Export to CSV/JSON
|
|
20
|
+
attune telemetry routing-stats Show adaptive routing statistics
|
|
21
|
+
attune telemetry routing-check Check for tier upgrade recommendations
|
|
22
|
+
attune telemetry models Show model performance by provider
|
|
23
|
+
attune telemetry agents Show active agents and their status
|
|
24
|
+
attune telemetry signals Show coordination signals for an agent
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
attune provider show Show current provider config
|
|
27
|
+
attune provider set <name> Set provider (anthropic, openai, hybrid)
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
attune validate Validate configuration
|
|
30
|
+
attune version Show version
|
|
31
31
|
|
|
32
32
|
For interactive development, use Claude Code skills:
|
|
33
33
|
/dev Developer tools (commit, review, debug, refactor)
|
|
@@ -58,7 +58,7 @@ def get_version() -> str:
|
|
|
58
58
|
try:
|
|
59
59
|
from importlib.metadata import version
|
|
60
60
|
|
|
61
|
-
return version("
|
|
61
|
+
return version("attune-ai")
|
|
62
62
|
except Exception: # noqa: BLE001
|
|
63
63
|
# INTENTIONAL: Fallback for dev installs without metadata
|
|
64
64
|
return "dev"
|
|
@@ -90,7 +90,7 @@ def cmd_workflow_list(args: Namespace) -> int:
|
|
|
90
90
|
|
|
91
91
|
print("-" * 60)
|
|
92
92
|
print(f"\nTotal: {len(workflows)} workflows")
|
|
93
|
-
print("\nRun a workflow:
|
|
93
|
+
print("\nRun a workflow: attune workflow run <name>")
|
|
94
94
|
return 0
|
|
95
95
|
|
|
96
96
|
|
|
@@ -429,7 +429,7 @@ def cmd_telemetry_routing_stats(args: Namespace) -> int:
|
|
|
429
429
|
|
|
430
430
|
except ImportError as e:
|
|
431
431
|
print(f"❌ Adaptive routing not available: {e}")
|
|
432
|
-
print(" Ensure
|
|
432
|
+
print(" Ensure attune-ai is installed with telemetry support")
|
|
433
433
|
return 1
|
|
434
434
|
except Exception as e: # noqa: BLE001
|
|
435
435
|
# INTENTIONAL: CLI commands should catch all errors and report gracefully
|
|
@@ -920,7 +920,7 @@ def cmd_validate(args: Namespace) -> int:
|
|
|
920
920
|
def cmd_version(args: Namespace) -> int:
|
|
921
921
|
"""Show version information."""
|
|
922
922
|
version = get_version()
|
|
923
|
-
print(f"
|
|
923
|
+
print(f"attune-ai {version}")
|
|
924
924
|
|
|
925
925
|
if args.verbose:
|
|
926
926
|
print(f"\nPython: {sys.version}")
|
|
@@ -930,7 +930,7 @@ def cmd_version(args: Namespace) -> int:
|
|
|
930
930
|
try:
|
|
931
931
|
from importlib.metadata import requires
|
|
932
932
|
|
|
933
|
-
reqs = requires("
|
|
933
|
+
reqs = requires("attune-ai") or []
|
|
934
934
|
print(f"\nDependencies: {len(reqs)}")
|
|
935
935
|
except Exception: # noqa: BLE001
|
|
936
936
|
pass
|
|
@@ -1073,8 +1073,12 @@ Documentation: https://smartaimemory.com/framework-docs/
|
|
|
1073
1073
|
|
|
1074
1074
|
# dashboard start
|
|
1075
1075
|
start_parser = dashboard_sub.add_parser("start", help="Start dashboard web server")
|
|
1076
|
-
start_parser.add_argument(
|
|
1077
|
-
|
|
1076
|
+
start_parser.add_argument(
|
|
1077
|
+
"--host", default="127.0.0.1", help="Host to bind to (default: 127.0.0.1)"
|
|
1078
|
+
)
|
|
1079
|
+
start_parser.add_argument(
|
|
1080
|
+
"--port", type=int, default=8000, help="Port to bind to (default: 8000)"
|
|
1081
|
+
)
|
|
1078
1082
|
|
|
1079
1083
|
# --- Utility commands ---
|
|
1080
1084
|
subparsers.add_parser("validate", help="Validate configuration")
|
|
@@ -1104,7 +1108,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1104
1108
|
elif args.workflow_command == "run":
|
|
1105
1109
|
return cmd_workflow_run(args)
|
|
1106
1110
|
else:
|
|
1107
|
-
print("Usage:
|
|
1111
|
+
print("Usage: attune workflow {list|info|run}")
|
|
1108
1112
|
return 1
|
|
1109
1113
|
|
|
1110
1114
|
elif args.command == "telemetry":
|
|
@@ -1125,7 +1129,9 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1125
1129
|
elif args.telemetry_command == "signals":
|
|
1126
1130
|
return cmd_telemetry_signals(args)
|
|
1127
1131
|
else:
|
|
1128
|
-
print(
|
|
1132
|
+
print(
|
|
1133
|
+
"Usage: attune telemetry {show|savings|export|routing-stats|routing-check|models|agents|signals}"
|
|
1134
|
+
)
|
|
1129
1135
|
return 1
|
|
1130
1136
|
|
|
1131
1137
|
elif args.command == "provider":
|
|
@@ -1134,14 +1140,14 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
1134
1140
|
elif args.provider_command == "set":
|
|
1135
1141
|
return cmd_provider_set(args)
|
|
1136
1142
|
else:
|
|
1137
|
-
print("Usage:
|
|
1143
|
+
print("Usage: attune provider {show|set}")
|
|
1138
1144
|
return 1
|
|
1139
1145
|
|
|
1140
1146
|
elif args.command == "dashboard":
|
|
1141
1147
|
if args.dashboard_command == "start":
|
|
1142
1148
|
return cmd_dashboard_start(args)
|
|
1143
1149
|
else:
|
|
1144
|
-
print("Usage:
|
|
1150
|
+
print("Usage: attune dashboard start [--host HOST] [--port PORT]")
|
|
1145
1151
|
return 1
|
|
1146
1152
|
|
|
1147
1153
|
elif args.command == "validate":
|