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.
- {empathy_framework-4.7.1.dist-info → empathy_framework-4.9.0.dist-info}/METADATA +65 -2
- {empathy_framework-4.7.1.dist-info → empathy_framework-4.9.0.dist-info}/RECORD +69 -59
- {empathy_framework-4.7.1.dist-info → empathy_framework-4.9.0.dist-info}/WHEEL +1 -1
- {empathy_framework-4.7.1.dist-info → empathy_framework-4.9.0.dist-info}/entry_points.txt +2 -1
- {empathy_framework-4.7.1.dist-info → empathy_framework-4.9.0.dist-info}/top_level.txt +0 -1
- empathy_os/__init__.py +2 -0
- empathy_os/cli/__init__.py +128 -238
- empathy_os/cli/__main__.py +5 -33
- empathy_os/cli/commands/__init__.py +1 -8
- empathy_os/cli/commands/help.py +331 -0
- empathy_os/cli/commands/info.py +140 -0
- empathy_os/cli/commands/inspect.py +437 -0
- empathy_os/cli/commands/metrics.py +92 -0
- empathy_os/cli/commands/orchestrate.py +184 -0
- empathy_os/cli/commands/patterns.py +207 -0
- empathy_os/cli/commands/provider.py +93 -81
- empathy_os/cli/commands/setup.py +96 -0
- empathy_os/cli/commands/status.py +235 -0
- empathy_os/cli/commands/sync.py +166 -0
- empathy_os/cli/commands/tier.py +121 -0
- empathy_os/cli/commands/workflow.py +574 -0
- empathy_os/cli/parsers/__init__.py +62 -0
- empathy_os/cli/parsers/help.py +41 -0
- empathy_os/cli/parsers/info.py +26 -0
- empathy_os/cli/parsers/inspect.py +66 -0
- empathy_os/cli/parsers/metrics.py +42 -0
- empathy_os/cli/parsers/orchestrate.py +61 -0
- empathy_os/cli/parsers/patterns.py +54 -0
- empathy_os/cli/parsers/provider.py +40 -0
- empathy_os/cli/parsers/setup.py +42 -0
- empathy_os/cli/parsers/status.py +47 -0
- empathy_os/cli/parsers/sync.py +31 -0
- empathy_os/cli/parsers/tier.py +33 -0
- empathy_os/cli/parsers/workflow.py +77 -0
- empathy_os/cli/utils/__init__.py +1 -0
- empathy_os/cli/utils/data.py +242 -0
- empathy_os/cli/utils/helpers.py +68 -0
- empathy_os/{cli.py → cli_legacy.py} +0 -26
- empathy_os/cli_minimal.py +662 -0
- empathy_os/cli_router.py +384 -0
- empathy_os/cli_unified.py +13 -2
- empathy_os/memory/short_term.py +146 -414
- empathy_os/memory/types.py +441 -0
- empathy_os/memory/unified.py +61 -48
- empathy_os/models/fallback.py +1 -1
- empathy_os/models/provider_config.py +59 -344
- empathy_os/models/registry.py +27 -176
- empathy_os/monitoring/alerts.py +14 -20
- empathy_os/monitoring/alerts_cli.py +24 -7
- empathy_os/project_index/__init__.py +2 -0
- empathy_os/project_index/index.py +210 -5
- empathy_os/project_index/scanner.py +48 -16
- empathy_os/project_index/scanner_parallel.py +291 -0
- empathy_os/workflow_commands.py +9 -9
- empathy_os/workflows/__init__.py +31 -2
- empathy_os/workflows/base.py +295 -317
- empathy_os/workflows/bug_predict.py +10 -2
- empathy_os/workflows/builder.py +273 -0
- empathy_os/workflows/caching.py +253 -0
- empathy_os/workflows/code_review_pipeline.py +1 -0
- empathy_os/workflows/history.py +512 -0
- empathy_os/workflows/perf_audit.py +129 -23
- empathy_os/workflows/routing.py +163 -0
- empathy_os/workflows/secure_release.py +1 -0
- empathy_os/workflows/security_audit.py +1 -0
- empathy_os/workflows/security_audit_phase3.py +352 -0
- empathy_os/workflows/telemetry_mixin.py +269 -0
- empathy_os/workflows/test_gen.py +7 -7
- empathy_os/dashboard/__init__.py +0 -15
- empathy_os/dashboard/server.py +0 -941
- empathy_os/vscode_bridge 2.py +0 -173
- empathy_os/workflows/progressive/README 2.md +0 -454
- empathy_os/workflows/progressive/__init__ 2.py +0 -92
- empathy_os/workflows/progressive/cli 2.py +0 -242
- empathy_os/workflows/progressive/core 2.py +0 -488
- empathy_os/workflows/progressive/orchestrator 2.py +0 -701
- empathy_os/workflows/progressive/reports 2.py +0 -528
- empathy_os/workflows/progressive/telemetry 2.py +0 -280
- empathy_os/workflows/progressive/test_gen 2.py +0 -514
- empathy_os/workflows/progressive/workflow 2.py +0 -628
- patterns/README.md +0 -119
- patterns/__init__.py +0 -95
- patterns/behavior.py +0 -298
- patterns/code_review_memory.json +0 -441
- patterns/core.py +0 -97
- patterns/debugging.json +0 -3763
- patterns/empathy.py +0 -268
- patterns/health_check_memory.json +0 -505
- patterns/input.py +0 -161
- patterns/memory_graph.json +0 -8
- patterns/refactoring_memory.json +0 -1113
- patterns/registry.py +0 -663
- patterns/security_memory.json +0 -8
- patterns/structural.py +0 -415
- patterns/validation.py +0 -194
- {empathy_framework-4.7.1.dist-info → empathy_framework-4.9.0.dist-info}/licenses/LICENSE +0 -0
empathy_os/cli_router.py
ADDED
|
@@ -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(
|
|
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
|
-
[
|
|
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
|
|