empathy-framework 4.7.1__py3-none-any.whl → 4.9.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.
Files changed (96) hide show
  1. {empathy_framework-4.7.1.dist-info → empathy_framework-4.9.0.dist-info}/METADATA +65 -2
  2. {empathy_framework-4.7.1.dist-info → empathy_framework-4.9.0.dist-info}/RECORD +69 -59
  3. {empathy_framework-4.7.1.dist-info → empathy_framework-4.9.0.dist-info}/WHEEL +1 -1
  4. {empathy_framework-4.7.1.dist-info → empathy_framework-4.9.0.dist-info}/entry_points.txt +2 -1
  5. {empathy_framework-4.7.1.dist-info → empathy_framework-4.9.0.dist-info}/top_level.txt +0 -1
  6. empathy_os/__init__.py +2 -0
  7. empathy_os/cli/__init__.py +128 -238
  8. empathy_os/cli/__main__.py +5 -33
  9. empathy_os/cli/commands/__init__.py +1 -8
  10. empathy_os/cli/commands/help.py +331 -0
  11. empathy_os/cli/commands/info.py +140 -0
  12. empathy_os/cli/commands/inspect.py +437 -0
  13. empathy_os/cli/commands/metrics.py +92 -0
  14. empathy_os/cli/commands/orchestrate.py +184 -0
  15. empathy_os/cli/commands/patterns.py +207 -0
  16. empathy_os/cli/commands/provider.py +93 -81
  17. empathy_os/cli/commands/setup.py +96 -0
  18. empathy_os/cli/commands/status.py +235 -0
  19. empathy_os/cli/commands/sync.py +166 -0
  20. empathy_os/cli/commands/tier.py +121 -0
  21. empathy_os/cli/commands/workflow.py +574 -0
  22. empathy_os/cli/parsers/__init__.py +62 -0
  23. empathy_os/cli/parsers/help.py +41 -0
  24. empathy_os/cli/parsers/info.py +26 -0
  25. empathy_os/cli/parsers/inspect.py +66 -0
  26. empathy_os/cli/parsers/metrics.py +42 -0
  27. empathy_os/cli/parsers/orchestrate.py +61 -0
  28. empathy_os/cli/parsers/patterns.py +54 -0
  29. empathy_os/cli/parsers/provider.py +40 -0
  30. empathy_os/cli/parsers/setup.py +42 -0
  31. empathy_os/cli/parsers/status.py +47 -0
  32. empathy_os/cli/parsers/sync.py +31 -0
  33. empathy_os/cli/parsers/tier.py +33 -0
  34. empathy_os/cli/parsers/workflow.py +77 -0
  35. empathy_os/cli/utils/__init__.py +1 -0
  36. empathy_os/cli/utils/data.py +242 -0
  37. empathy_os/cli/utils/helpers.py +68 -0
  38. empathy_os/{cli.py → cli_legacy.py} +0 -26
  39. empathy_os/cli_minimal.py +662 -0
  40. empathy_os/cli_router.py +384 -0
  41. empathy_os/cli_unified.py +13 -2
  42. empathy_os/memory/short_term.py +146 -414
  43. empathy_os/memory/types.py +441 -0
  44. empathy_os/memory/unified.py +61 -48
  45. empathy_os/models/fallback.py +1 -1
  46. empathy_os/models/provider_config.py +59 -344
  47. empathy_os/models/registry.py +27 -176
  48. empathy_os/monitoring/alerts.py +14 -20
  49. empathy_os/monitoring/alerts_cli.py +24 -7
  50. empathy_os/project_index/__init__.py +2 -0
  51. empathy_os/project_index/index.py +210 -5
  52. empathy_os/project_index/scanner.py +48 -16
  53. empathy_os/project_index/scanner_parallel.py +291 -0
  54. empathy_os/workflow_commands.py +9 -9
  55. empathy_os/workflows/__init__.py +31 -2
  56. empathy_os/workflows/base.py +295 -317
  57. empathy_os/workflows/bug_predict.py +10 -2
  58. empathy_os/workflows/builder.py +273 -0
  59. empathy_os/workflows/caching.py +253 -0
  60. empathy_os/workflows/code_review_pipeline.py +1 -0
  61. empathy_os/workflows/history.py +512 -0
  62. empathy_os/workflows/perf_audit.py +129 -23
  63. empathy_os/workflows/routing.py +163 -0
  64. empathy_os/workflows/secure_release.py +1 -0
  65. empathy_os/workflows/security_audit.py +1 -0
  66. empathy_os/workflows/security_audit_phase3.py +352 -0
  67. empathy_os/workflows/telemetry_mixin.py +269 -0
  68. empathy_os/workflows/test_gen.py +7 -7
  69. empathy_os/dashboard/__init__.py +0 -15
  70. empathy_os/dashboard/server.py +0 -941
  71. empathy_os/vscode_bridge 2.py +0 -173
  72. empathy_os/workflows/progressive/README 2.md +0 -454
  73. empathy_os/workflows/progressive/__init__ 2.py +0 -92
  74. empathy_os/workflows/progressive/cli 2.py +0 -242
  75. empathy_os/workflows/progressive/core 2.py +0 -488
  76. empathy_os/workflows/progressive/orchestrator 2.py +0 -701
  77. empathy_os/workflows/progressive/reports 2.py +0 -528
  78. empathy_os/workflows/progressive/telemetry 2.py +0 -280
  79. empathy_os/workflows/progressive/test_gen 2.py +0 -514
  80. empathy_os/workflows/progressive/workflow 2.py +0 -628
  81. patterns/README.md +0 -119
  82. patterns/__init__.py +0 -95
  83. patterns/behavior.py +0 -298
  84. patterns/code_review_memory.json +0 -441
  85. patterns/core.py +0 -97
  86. patterns/debugging.json +0 -3763
  87. patterns/empathy.py +0 -268
  88. patterns/health_check_memory.json +0 -505
  89. patterns/input.py +0 -161
  90. patterns/memory_graph.json +0 -8
  91. patterns/refactoring_memory.json +0 -1113
  92. patterns/registry.py +0 -663
  93. patterns/security_memory.json +0 -8
  94. patterns/structural.py +0 -415
  95. patterns/validation.py +0 -194
  96. {empathy_framework-4.7.1.dist-info → empathy_framework-4.9.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,384 @@
1
+ """Hybrid CLI Router - Slash Commands + Natural Language
2
+
3
+ Supports both structured slash commands and natural language routing:
4
+ - Slash commands: empathy /dev commit
5
+ - Natural language: empathy "commit my changes"
6
+ - Single word: empathy commit (infers /dev commit)
7
+
8
+ Copyright 2025 Smart-AI-Memory
9
+ Licensed under Fair Source License 0.9
10
+ """
11
+
12
+ import os
13
+ import re
14
+ from dataclasses import dataclass
15
+ from pathlib import Path
16
+ from typing import Any
17
+
18
+ import yaml
19
+
20
+ from empathy_os.routing import SmartRouter
21
+
22
+
23
+ @dataclass
24
+ class RoutingPreference:
25
+ """User's learned routing preferences."""
26
+
27
+ keyword: str
28
+ slash_command: str
29
+ usage_count: int = 0
30
+ confidence: float = 1.0
31
+
32
+
33
+ class HybridRouter:
34
+ """Routes user input to appropriate commands or workflows.
35
+
36
+ Supports three input modes:
37
+ 1. Slash commands: /dev commit (direct, structured)
38
+ 2. Single words: commit (infers /dev commit from context)
39
+ 3. Natural language: "I need to commit" (uses SmartRouter)
40
+
41
+ Example:
42
+ router = HybridRouter()
43
+
44
+ # Direct slash command
45
+ result = await router.route("/dev commit")
46
+ # → {type: "slash", hub: "dev", command: "commit"}
47
+
48
+ # Single word inference
49
+ result = await router.route("commit")
50
+ # → {type: "inferred", hub: "dev", command: "commit", confidence: 0.9}
51
+
52
+ # Natural language
53
+ result = await router.route("I want to commit my changes")
54
+ # → {type: "natural", workflow: "commit", slash_equivalent: "/dev commit"}
55
+ """
56
+
57
+ def __init__(self, preferences_path: str | None = None):
58
+ """Initialize hybrid router.
59
+
60
+ Args:
61
+ preferences_path: Path to user preferences YAML
62
+ Default: .empathy/routing_preferences.yaml
63
+ """
64
+ self.preferences_path = Path(
65
+ preferences_path or Path.home() / ".empathy" / "routing_preferences.yaml"
66
+ )
67
+ self.smart_router = SmartRouter()
68
+ self.preferences: dict[str, RoutingPreference] = {}
69
+
70
+ # Command to slash command mapping
71
+ self._command_map = {
72
+ # Dev commands
73
+ "commit": "/dev commit",
74
+ "review": "/dev review-pr",
75
+ "review-pr": "/dev review-pr",
76
+ "refactor": "/dev refactor",
77
+ "perf": "/dev perf-audit",
78
+ "perf-audit": "/dev perf-audit",
79
+ # Testing commands
80
+ "test": "/testing run",
81
+ "tests": "/testing run",
82
+ "coverage": "/testing coverage",
83
+ "generate-tests": "/testing gen",
84
+ "test-gen": "/testing gen",
85
+ # Learning commands
86
+ "evaluate": "/learning evaluate",
87
+ "patterns": "/learning patterns",
88
+ "improve": "/learning improve",
89
+ # Workflow commands
90
+ "security": "/workflows security-audit",
91
+ "security-audit": "/workflows security-audit",
92
+ "bug-predict": "/workflows bug-predict",
93
+ "bugs": "/workflows bug-predict",
94
+ # Context commands
95
+ "status": "/context status",
96
+ "memory": "/context memory",
97
+ "state": "/context state",
98
+ # Doc commands
99
+ "explain": "/docs explain",
100
+ "document": "/docs generate",
101
+ "overview": "/docs overview",
102
+ }
103
+
104
+ # Hub descriptions for disambiguation
105
+ self._hub_descriptions = {
106
+ "dev": "Development tools (commits, reviews, refactoring)",
107
+ "testing": "Test generation and coverage analysis",
108
+ "learning": "Session evaluation and pattern learning",
109
+ "workflows": "AI-powered workflows (security, bugs, performance)",
110
+ "context": "Memory and state management",
111
+ "docs": "Documentation generation",
112
+ "plan": "Development planning and architecture",
113
+ "release": "Release preparation and publishing",
114
+ "utilities": "Utility tools (profiling, dependencies)",
115
+ }
116
+
117
+ self._load_preferences()
118
+
119
+ def _load_preferences(self) -> None:
120
+ """Load user routing preferences from disk."""
121
+ if not self.preferences_path.exists():
122
+ return
123
+
124
+ try:
125
+ with open(self.preferences_path) as f:
126
+ data = yaml.safe_load(f) or {}
127
+
128
+ for keyword, pref_data in data.get("preferences", {}).items():
129
+ self.preferences[keyword] = RoutingPreference(
130
+ keyword=keyword,
131
+ slash_command=pref_data["slash_command"],
132
+ usage_count=pref_data.get("usage_count", 0),
133
+ confidence=pref_data.get("confidence", 1.0),
134
+ )
135
+ except Exception as e:
136
+ print(f"Warning: Could not load routing preferences: {e}")
137
+
138
+ def _save_preferences(self) -> None:
139
+ """Save user routing preferences to disk."""
140
+ self.preferences_path.parent.mkdir(parents=True, exist_ok=True)
141
+
142
+ data = {
143
+ "preferences": {
144
+ pref.keyword: {
145
+ "slash_command": pref.slash_command,
146
+ "usage_count": pref.usage_count,
147
+ "confidence": pref.confidence,
148
+ }
149
+ for pref in self.preferences.values()
150
+ }
151
+ }
152
+
153
+ with open(self.preferences_path, "w") as f:
154
+ yaml.dump(data, f, default_flow_style=False)
155
+
156
+ async def route(
157
+ self, user_input: str, context: dict[str, Any] | None = None
158
+ ) -> dict[str, Any]:
159
+ """Route user input to appropriate command or workflow.
160
+
161
+ Args:
162
+ user_input: User's input (slash command, keyword, or natural language)
163
+ context: Optional context (current file, project info, etc.)
164
+
165
+ Returns:
166
+ Routing result with type, command/workflow, and metadata
167
+ """
168
+ user_input = user_input.strip()
169
+
170
+ # Level 1: Slash command (direct execution)
171
+ if user_input.startswith("/"):
172
+ return self._route_slash_command(user_input)
173
+
174
+ # Level 2: Single word or known command (inference)
175
+ words = user_input.split()
176
+ if len(words) <= 2:
177
+ inferred = self._infer_command(user_input)
178
+ if inferred:
179
+ return inferred
180
+
181
+ # Level 3: Natural language (SmartRouter)
182
+ return await self._route_natural_language(user_input, context)
183
+
184
+ def _route_slash_command(self, command: str) -> dict[str, Any]:
185
+ """Route slash command directly.
186
+
187
+ Args:
188
+ command: Slash command like "/dev commit"
189
+
190
+ Returns:
191
+ Routing result
192
+ """
193
+ parts = command[1:].split(maxsplit=1) # Remove leading /
194
+ hub = parts[0] if parts else "help"
195
+ subcommand = parts[1] if len(parts) > 1 else None
196
+
197
+ return {
198
+ "type": "slash",
199
+ "hub": hub,
200
+ "command": subcommand,
201
+ "original": command,
202
+ "confidence": 1.0,
203
+ }
204
+
205
+ def _infer_command(self, keyword: str) -> dict[str, Any] | None:
206
+ """Infer slash command from keyword or short phrase.
207
+
208
+ Args:
209
+ keyword: Single word or short phrase
210
+
211
+ Returns:
212
+ Routing result if inference successful, None otherwise
213
+ """
214
+ keyword_lower = keyword.lower().strip()
215
+
216
+ # Check learned preferences first
217
+ if keyword_lower in self.preferences:
218
+ pref = self.preferences[keyword_lower]
219
+ slash_cmd = pref.slash_command
220
+
221
+ # Update usage count
222
+ pref.usage_count += 1
223
+ self._save_preferences()
224
+
225
+ return self._parse_inferred(slash_cmd, keyword, pref.confidence, "learned")
226
+
227
+ # Check built-in command map
228
+ if keyword_lower in self._command_map:
229
+ slash_cmd = self._command_map[keyword_lower]
230
+ return self._parse_inferred(slash_cmd, keyword, 0.9, "builtin")
231
+
232
+ # Check for hub names (show hub menu)
233
+ if keyword_lower in self._hub_descriptions:
234
+ return {
235
+ "type": "hub_menu",
236
+ "hub": keyword_lower,
237
+ "original": keyword,
238
+ "confidence": 1.0,
239
+ }
240
+
241
+ return None
242
+
243
+ def _parse_inferred(
244
+ self, slash_cmd: str, original: str, confidence: float, source: str
245
+ ) -> dict[str, Any]:
246
+ """Parse inferred slash command."""
247
+ parts = slash_cmd[1:].split(maxsplit=1) # Remove leading /
248
+ hub = parts[0] if parts else "help"
249
+ subcommand = parts[1] if len(parts) > 1 else None
250
+
251
+ return {
252
+ "type": "inferred",
253
+ "hub": hub,
254
+ "command": subcommand,
255
+ "original": original,
256
+ "slash_equivalent": slash_cmd,
257
+ "confidence": confidence,
258
+ "source": source, # learned, builtin
259
+ }
260
+
261
+ async def _route_natural_language(
262
+ self, text: str, context: dict[str, Any] | None = None
263
+ ) -> dict[str, Any]:
264
+ """Route natural language input using SmartRouter.
265
+
266
+ Args:
267
+ text: Natural language input
268
+ context: Optional context
269
+
270
+ Returns:
271
+ Routing result with workflow and slash equivalent
272
+ """
273
+ # Use SmartRouter for classification
274
+ decision = await self.smart_router.route(text, context)
275
+
276
+ # Map workflow to slash command
277
+ slash_equivalent = self._workflow_to_slash(decision.primary_workflow)
278
+
279
+ return {
280
+ "type": "natural",
281
+ "workflow": decision.primary_workflow,
282
+ "secondary_workflows": decision.secondary_workflows,
283
+ "slash_equivalent": slash_equivalent,
284
+ "confidence": decision.confidence,
285
+ "reasoning": decision.reasoning,
286
+ "original": text,
287
+ }
288
+
289
+ def _workflow_to_slash(self, workflow: str) -> str:
290
+ """Map workflow name to slash command equivalent.
291
+
292
+ Args:
293
+ workflow: Workflow name (e.g., "security-audit")
294
+
295
+ Returns:
296
+ Slash command equivalent
297
+ """
298
+ # Workflow to slash command mapping
299
+ workflow_map = {
300
+ "security-audit": "/workflows security-audit",
301
+ "bug-predict": "/workflows bug-predict",
302
+ "code-review": "/dev review",
303
+ "test-gen": "/testing gen",
304
+ "perf-audit": "/dev perf-audit",
305
+ "commit": "/dev commit",
306
+ "refactor": "/dev refactor",
307
+ }
308
+
309
+ return workflow_map.get(workflow, f"/workflows {workflow}")
310
+
311
+ def learn_preference(self, keyword: str, slash_command: str) -> None:
312
+ """Learn user's routing preference.
313
+
314
+ Args:
315
+ keyword: Keyword user typed
316
+ slash_command: Command that was executed
317
+ """
318
+ if keyword in self.preferences:
319
+ pref = self.preferences[keyword]
320
+ pref.usage_count += 1
321
+ # Increase confidence with repeated usage
322
+ pref.confidence = min(1.0, pref.confidence + 0.05)
323
+ else:
324
+ self.preferences[keyword] = RoutingPreference(
325
+ keyword=keyword,
326
+ slash_command=slash_command,
327
+ usage_count=1,
328
+ confidence=0.8,
329
+ )
330
+
331
+ self._save_preferences()
332
+
333
+ def get_suggestions(self, partial: str) -> list[str]:
334
+ """Get command suggestions based on partial input.
335
+
336
+ Args:
337
+ partial: Partial command input
338
+
339
+ Returns:
340
+ List of suggested commands
341
+ """
342
+ suggestions = []
343
+ partial_lower = partial.lower()
344
+
345
+ # Suggest slash commands
346
+ for cmd in self._command_map.values():
347
+ if partial_lower in cmd.lower():
348
+ suggestions.append(cmd)
349
+
350
+ # Suggest learned preferences
351
+ for pref in self.preferences.values():
352
+ if partial_lower in pref.keyword.lower():
353
+ suggestions.append(f"{pref.keyword} → {pref.slash_command}")
354
+
355
+ return suggestions[:5] # Top 5 suggestions
356
+
357
+
358
+ # Convenience functions
359
+ async def route_user_input(
360
+ user_input: str, context: dict[str, Any] | None = None
361
+ ) -> dict[str, Any]:
362
+ """Quick routing helper.
363
+
364
+ Args:
365
+ user_input: User's input
366
+ context: Optional context
367
+
368
+ Returns:
369
+ Routing result
370
+ """
371
+ router = HybridRouter()
372
+ return await router.route(user_input, context)
373
+
374
+
375
+ def is_slash_command(text: str) -> bool:
376
+ """Check if text is a slash command.
377
+
378
+ Args:
379
+ text: Input text
380
+
381
+ Returns:
382
+ True if slash command, False otherwise
383
+ """
384
+ return text.strip().startswith("/")
empathy_os/cli_unified.py CHANGED
@@ -542,7 +542,9 @@ def utilities_status():
542
542
  @utilities_app.command("scan")
543
543
  def utilities_scan(
544
544
  path: Path = typer.Argument(Path("."), help="Path to scan"),
545
- scan_type: str = typer.Option("all", "--type", "-t", help="Scan type: security, performance, or all"),
545
+ scan_type: str = typer.Option(
546
+ "all", "--type", "-t", help="Scan type: security, performance, or all"
547
+ ),
546
548
  ):
547
549
  """Scan codebase for issues.
548
550
 
@@ -649,7 +651,16 @@ def telemetry_export(
649
651
  ):
650
652
  """Export telemetry data."""
651
653
  subprocess.run(
652
- [sys.executable, "-m", "empathy_os.cli", "telemetry", "export", str(output), "--format", format_type],
654
+ [
655
+ sys.executable,
656
+ "-m",
657
+ "empathy_os.cli",
658
+ "telemetry",
659
+ "export",
660
+ str(output),
661
+ "--format",
662
+ format_type,
663
+ ],
653
664
  check=False,
654
665
  )
655
666