hanuscode 1.0.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 (93) hide show
  1. hanus/__init__.py +5 -0
  2. hanus/__main__.py +10 -0
  3. hanus/action_handlers.py +76 -0
  4. hanus/action_parser.py +82 -0
  5. hanus/agent_runner.py +1445 -0
  6. hanus/analysis/__init__.py +5 -0
  7. hanus/analysis/debt.py +702 -0
  8. hanus/analysis/dependencies.py +475 -0
  9. hanus/cache/__init__.py +5 -0
  10. hanus/cache/response_cache.py +560 -0
  11. hanus/config.py +401 -0
  12. hanus/connectors/__init__.py +19 -0
  13. hanus/connectors/base.py +114 -0
  14. hanus/connectors/claude_connector.py +146 -0
  15. hanus/connectors/gemini_connector.py +141 -0
  16. hanus/connectors/glm_connector.py +160 -0
  17. hanus/connectors/ollama_connector.py +174 -0
  18. hanus/connectors/openai_connector.py +122 -0
  19. hanus/connectors/registry.py +26 -0
  20. hanus/context/__init__.py +7 -0
  21. hanus/context/manager.py +837 -0
  22. hanus/context/selective.py +626 -0
  23. hanus/error_recovery/__init__.py +5 -0
  24. hanus/error_recovery/auto_fix.py +605 -0
  25. hanus/hooks/__init__.py +5 -0
  26. hanus/hooks/manager.py +247 -0
  27. hanus/instincts/__init__.py +44 -0
  28. hanus/instincts/cli.py +372 -0
  29. hanus/instincts/detector.py +281 -0
  30. hanus/instincts/evolver.py +361 -0
  31. hanus/instincts/manager.py +343 -0
  32. hanus/instincts/types.py +253 -0
  33. hanus/logger.py +81 -0
  34. hanus/memory/__init__.py +8 -0
  35. hanus/memory/manager.py +265 -0
  36. hanus/memory/types.py +119 -0
  37. hanus/monitor.py +341 -0
  38. hanus/parallel/__init__.py +5 -0
  39. hanus/parallel/executor.py +300 -0
  40. hanus/permissions.py +182 -0
  41. hanus/plan/__init__.py +8 -0
  42. hanus/plan/mode.py +267 -0
  43. hanus/plan/models.py +152 -0
  44. hanus/plugin_manager.py +754 -0
  45. hanus/plugin_registry.py +391 -0
  46. hanus/plugins/__init__.py +1 -0
  47. hanus/plugins/arena.py +630 -0
  48. hanus/plugins/code_review.py +123 -0
  49. hanus/plugins/cortex.py +1750 -0
  50. hanus/plugins/deps_check.py +27 -0
  51. hanus/plugins/git_ops.py +33 -0
  52. hanus/plugins/metasploit.py +530 -0
  53. hanus/plugins/notes.py +583 -0
  54. hanus/plugins/search_code.py +59 -0
  55. hanus/plugins/searchsploit.py +495 -0
  56. hanus/plugins/strategist.py +175 -0
  57. hanus/plugins/webui.py +5200 -0
  58. hanus/profiles.py +479 -0
  59. hanus/profiles_builtin/__init__.py +0 -0
  60. hanus/profiles_builtin/architect/profile.yaml +12 -0
  61. hanus/profiles_builtin/architect/system_prompt.txt +71 -0
  62. hanus/profiles_builtin/deep/profile.yaml +12 -0
  63. hanus/profiles_builtin/deep/system_prompt.txt +66 -0
  64. hanus/profiles_builtin/developer/__init__.py +0 -0
  65. hanus/profiles_builtin/developer/profile.yaml +9 -0
  66. hanus/profiles_builtin/developer/system_prompt.txt +176 -0
  67. hanus/profiles_builtin/speed/profile.yaml +12 -0
  68. hanus/profiles_builtin/speed/system_prompt.txt +51 -0
  69. hanus/project_tools.py +177 -0
  70. hanus/query_engine.py +1594 -0
  71. hanus/rules/__init__.py +237 -0
  72. hanus/search/__init__.py +5 -0
  73. hanus/search/semantic.py +596 -0
  74. hanus/session_manager.py +547 -0
  75. hanus/skill_manager.py +702 -0
  76. hanus/skills/__init__.py +4 -0
  77. hanus/subagent/__init__.py +8 -0
  78. hanus/subagent/agents/__init__.py +253 -0
  79. hanus/subagent/manager.py +309 -0
  80. hanus/subagent/types.py +266 -0
  81. hanus/suggestions/__init__.py +5 -0
  82. hanus/suggestions/proactive.py +451 -0
  83. hanus/tasks/__init__.py +8 -0
  84. hanus/tasks/manager.py +330 -0
  85. hanus/tasks/models.py +106 -0
  86. hanus/terminal_prompt.py +166 -0
  87. hanus/tools.py +1849 -0
  88. hanus/ui.py +939 -0
  89. hanuscode-1.0.0.dist-info/METADATA +1151 -0
  90. hanuscode-1.0.0.dist-info/RECORD +93 -0
  91. hanuscode-1.0.0.dist-info/WHEEL +5 -0
  92. hanuscode-1.0.0.dist-info/entry_points.txt +2 -0
  93. hanuscode-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,281 @@
1
+ # hanus/instincts/detector.py
2
+ """
3
+ Detector de patrones para crear instintos desde sesiones.
4
+
5
+ Analiza las interacciones y detecta patrones que pueden convertirse
6
+ en instintos.
7
+ """
8
+ from __future__ import annotations
9
+ import re
10
+ from dataclasses import dataclass, field
11
+ from typing import List, Dict, Optional, Set, Tuple
12
+ from collections import Counter
13
+ from datetime import datetime
14
+
15
+ from hanus.instincts.types import (
16
+ Instinct, InstinctScope, InstinctDomain, InstinctEvidence
17
+ )
18
+
19
+
20
+ @dataclass
21
+ class SessionAction:
22
+ """Acción realizada en una sesión."""
23
+ timestamp: str
24
+ tool: str # Herramienta usada (read, write, edit, bash, etc.)
25
+ action: str # Acción específica
26
+ context: str # Contexto (archivo, comando, etc.)
27
+ outcome: str # success/failure
28
+ metadata: Dict = field(default_factory=dict)
29
+
30
+
31
+ @dataclass
32
+ class Pattern:
33
+ """Patrón detectado en las acciones."""
34
+ name: str
35
+ trigger: str # Cuándo aplicar
36
+ actions: List[str] # Secuencia de acciones
37
+ frequency: int # Frecuencia de aparición
38
+ confidence: float # Confianza del patrón
39
+ domain: InstinctDomain
40
+ evidence: List[SessionAction] = field(default_factory=list)
41
+
42
+
43
+ class PatternDetector:
44
+ """
45
+ Detecta patrones en sesiones que pueden convertirse en instintos.
46
+
47
+ Tipos de patrones detectados:
48
+ 1. WORKFLOW: Secuencia de acciones repetidas
49
+ 2. ERROR_RECOVERY: Respuestas a errores
50
+ 3. CODE_STYLE: Preferencias de código
51
+ 4. SECURITY: Comportamientos de seguridad
52
+ 5. TESTING: Patrones de testing
53
+ """
54
+
55
+ # Patrones predefinidos que buscamos
56
+ PREDEFINED_PATTERNS = {
57
+ "read_before_edit": {
58
+ "trigger": "when modifying code",
59
+ "domain": InstinctDomain.WORKFLOW,
60
+ "pattern": [
61
+ ("read", "before", "edit"),
62
+ ],
63
+ },
64
+ "test_after_write": {
65
+ "trigger": "when writing new code",
66
+ "domain": InstinctDomain.TESTING,
67
+ "pattern": [
68
+ ("write", "then", "test"),
69
+ ],
70
+ },
71
+ "validate_input": {
72
+ "trigger": "when handling user input",
73
+ "domain": InstinctDomain.SECURITY,
74
+ "pattern": [
75
+ ("validate", "before", "process"),
76
+ ],
77
+ },
78
+ "grep_for_context": {
79
+ "trigger": "when understanding codebase",
80
+ "domain": InstinctDomain.WORKFLOW,
81
+ "pattern": [
82
+ ("grep", "before", "modify"),
83
+ ],
84
+ },
85
+ "commit_after_changes": {
86
+ "trigger": "when completing feature",
87
+ "domain": InstinctDomain.GIT,
88
+ "pattern": [
89
+ ("test", "then", "commit"),
90
+ ],
91
+ },
92
+ "check_types": {
93
+ "trigger": "when modifying TypeScript/Python",
94
+ "domain": InstinctDomain.WORKFLOW,
95
+ "pattern": [
96
+ ("edit", "then", "typecheck"),
97
+ ],
98
+ },
99
+ "security_review": {
100
+ "trigger": "when handling sensitive data",
101
+ "domain": InstinctDomain.SECURITY,
102
+ "pattern": [
103
+ ("check", "secrets", "before"),
104
+ ("validate", "input", "always"),
105
+ ],
106
+ },
107
+ }
108
+
109
+ def __init__(self, min_frequency: int = 2, min_confidence: float = 0.3):
110
+ self.min_frequency = min_frequency
111
+ self.min_confidence = min_confidence
112
+ self._actions: List[SessionAction] = []
113
+ self._detected_patterns: Dict[str, Pattern] = {}
114
+
115
+ def record_action(self, action: SessionAction) -> None:
116
+ """Registra una acción para análisis."""
117
+ self._actions.append(action)
118
+
119
+ def analyze_session(self, actions: List[SessionAction]) -> List[Pattern]:
120
+ """
121
+ Analiza una sesión y detecta patrones.
122
+
123
+ Returns:
124
+ Lista de patrones detectados
125
+ """
126
+ self._actions = actions
127
+ patterns = []
128
+
129
+ # Detectar patrones predefinidos
130
+ for pattern_name, pattern_def in self.PREDEFINED_PATTERNS.items():
131
+ detected = self._detect_pattern(pattern_name, pattern_def)
132
+ if detected:
133
+ patterns.append(detected)
134
+
135
+ # Detectar patrones de secuencia
136
+ sequence_patterns = self._detect_sequence_patterns()
137
+ patterns.extend(sequence_patterns)
138
+
139
+ # Detectar patrones de error/recovery
140
+ error_patterns = self._detect_error_recovery_patterns()
141
+ patterns.extend(error_patterns)
142
+
143
+ return patterns
144
+
145
+ def _detect_pattern(self, name: str, definition: Dict) -> Optional[Pattern]:
146
+ """Detecta si un patrón predefinido aparece en las acciones."""
147
+ matching_actions = []
148
+
149
+ for action in self._actions:
150
+ # Verificar si la acción coincide con el patrón
151
+ for pattern_part in definition["pattern"]:
152
+ if self._action_matches_pattern(action, pattern_part):
153
+ matching_actions.append(action)
154
+
155
+ # Calcular frecuencia y confianza
156
+ frequency = len(matching_actions)
157
+ if frequency < self.min_frequency:
158
+ return None
159
+
160
+ # Calcular confianza basada en frecuencia y consistencia
161
+ confidence = min(0.9, 0.3 + (frequency * 0.1))
162
+
163
+ return Pattern(
164
+ name=name,
165
+ trigger=definition["trigger"],
166
+ actions=[a.action for a in matching_actions],
167
+ frequency=frequency,
168
+ confidence=confidence,
169
+ domain=definition["domain"],
170
+ evidence=matching_actions,
171
+ )
172
+
173
+ def _action_matches_pattern(self, action: SessionAction, pattern_part: Tuple) -> bool:
174
+ """Verifica si una acción coincide con un patrón."""
175
+ # Implementación simplificada
176
+ tool_match = pattern_part[0] in action.tool.lower()
177
+ action_match = pattern_part[1] in action.action.lower()
178
+ return tool_match and action_match
179
+
180
+ def _detect_sequence_patterns(self) -> List[Pattern]:
181
+ """Detecta patrones de secuencia (acciones que ocurren en orden)."""
182
+ patterns = []
183
+
184
+ # Buscar secuencias de 2-4 acciones que se repiten
185
+ for length in range(2, 5):
186
+ sequences = []
187
+ for i in range(len(self._actions) - length + 1):
188
+ seq = tuple(a.tool for a in self._actions[i:i+length])
189
+ sequences.append(seq)
190
+
191
+ # Contar frecuencias
192
+ counter = Counter(sequences)
193
+
194
+ for seq, freq in counter.most_common():
195
+ if freq >= self.min_frequency:
196
+ # Generar patrón
197
+ pattern_name = "-".join(seq)
198
+ trigger = f"when performing {seq[0]}"
199
+
200
+ patterns.append(Pattern(
201
+ name=pattern_name,
202
+ trigger=trigger,
203
+ actions=list(seq),
204
+ frequency=freq,
205
+ confidence=min(0.9, 0.3 + (freq * 0.1)),
206
+ domain=InstinctDomain.WORKFLOW,
207
+ evidence=[a for a in self._actions if a.tool in seq],
208
+ ))
209
+
210
+ return patterns
211
+
212
+ def _detect_error_recovery_patterns(self) -> List[Pattern]:
213
+ """Detecta patrones de recuperación de errores."""
214
+ patterns = []
215
+
216
+ # Buscar fallos seguidos de acciones correctivas
217
+ for i, action in enumerate(self._actions):
218
+ if action.outcome == "failure":
219
+ # Ver qué acciones siguen al fallo
220
+ recovery_actions = []
221
+ for j in range(i + 1, min(i + 5, len(self._actions))):
222
+ if self._actions[j].outcome == "success":
223
+ recovery_actions.append(self._actions[j])
224
+
225
+ if recovery_actions:
226
+ pattern_name = f"recover-from-{action.tool}"
227
+ patterns.append(Pattern(
228
+ name=pattern_name,
229
+ trigger=f"when {action.tool} fails",
230
+ actions=[a.action for a in recovery_actions],
231
+ frequency=1,
232
+ confidence=0.5,
233
+ domain=InstinctDomain.DEBUGGING,
234
+ evidence=[action] + recovery_actions,
235
+ ))
236
+
237
+ return patterns
238
+
239
+ def create_instinct_from_pattern(self, pattern: Pattern,
240
+ scope: InstinctScope = InstinctScope.PROJECT) -> Instinct:
241
+ """Crea un instinto desde un patrón detectado."""
242
+ evidence = [
243
+ InstinctEvidence(
244
+ timestamp=a.timestamp,
245
+ action=a.action,
246
+ context=a.context,
247
+ outcome=a.outcome,
248
+ file_path=a.metadata.get("file_path"),
249
+ snippet=a.metadata.get("snippet"),
250
+ )
251
+ for a in pattern.evidence
252
+ ]
253
+
254
+ return Instinct(
255
+ id=Instinct.generate_id(pattern.name, pattern.trigger),
256
+ name=pattern.name,
257
+ description=f"Learned behavior: {pattern.trigger}",
258
+ trigger=pattern.trigger,
259
+ action=", ".join(pattern.actions[:3]), # Top 3 actions
260
+ domain=pattern.domain,
261
+ scope=scope,
262
+ confidence=pattern.confidence,
263
+ evidence=evidence,
264
+ observations=pattern.frequency,
265
+ successes=pattern.frequency, # Asumimos éxito
266
+ failures=0,
267
+ )
268
+
269
+ def get_pattern_summary(self) -> Dict:
270
+ """Obtiene un resumen de los patrones detectados."""
271
+ return {
272
+ "total_actions": len(self._actions),
273
+ "patterns_detected": len(self._detected_patterns),
274
+ "by_domain": {
275
+ domain.value: len([p for p in self._detected_patterns.values()
276
+ if p.domain == domain])
277
+ for domain in InstinctDomain
278
+ },
279
+ "high_confidence": len([p for p in self._detected_patterns.values()
280
+ if p.confidence >= 0.8]),
281
+ }
@@ -0,0 +1,361 @@
1
+ # hanus/instincts/evolver.py
2
+ """
3
+ Evolucionador de instintos.
4
+
5
+ Analiza instintos y los agrupa en estructuras de mayor nivel:
6
+ - Skills: Comportamientos auto-triggered
7
+ - Commands: Acciones invocadas por usuario
8
+ - Agents: Procesos multi-paso complejos
9
+ """
10
+ from __future__ import annotations
11
+ from dataclasses import dataclass, field
12
+ from typing import List, Dict, Optional, Tuple
13
+ from collections import defaultdict
14
+ from datetime import datetime
15
+
16
+ from hanus.instincts.types import (
17
+ Instinct, InstinctScope, InstinctDomain, InstinctStatus
18
+ )
19
+ from hanus.instincts.manager import InstinctManager
20
+
21
+
22
+ @dataclass
23
+ class EvolutionCandidate:
24
+ """Candidato para evolución."""
25
+ name: str
26
+ type: str # "skill", "command", "agent"
27
+ instincts: List[Instinct]
28
+ avg_confidence: float
29
+ domains: List[InstinctDomain]
30
+ scope: InstinctScope
31
+ reason: str # Por qué es candidato
32
+
33
+
34
+ @dataclass
35
+ class EvolvedStructure:
36
+ """Estructura evolucionada."""
37
+ name: str
38
+ type: str # "skill", "command", "agent"
39
+ description: str
40
+ instincts: List[str] # IDs de instintos origen
41
+ content: str # Contenido generado
42
+ scope: InstinctScope
43
+ created_at: str = field(default_factory=lambda: datetime.now().isoformat())
44
+
45
+
46
+ class InstinctEvolver:
47
+ """
48
+ Analiza instintos y sugiere o genera estructuras evolucionadas.
49
+
50
+ Reglas de evolución:
51
+ - → Command: Cuando instintos describen acciones invocadas por usuario
52
+ - → Skill: Cuando instintos describen comportamientos auto-triggered
53
+ - → Agent: Cuando instintos describen procesos multi-paso complejos
54
+ """
55
+
56
+ # Umbrales
57
+ MIN_INSTINCTS_FOR_SKILL = 2
58
+ MIN_INSTINCTS_FOR_COMMAND = 2
59
+ MIN_INSTINCTS_FOR_AGENT = 3
60
+ MIN_CONFIDENCE = 0.7
61
+
62
+ def __init__(self, manager: InstinctManager):
63
+ self.manager = manager
64
+
65
+ def analyze(self, min_confidence: float = None) -> List[EvolutionCandidate]:
66
+ """
67
+ Analiza instintos y encuentra candidatos para evolución.
68
+
69
+ Returns:
70
+ Lista de candidatos de evolución
71
+ """
72
+ min_conf = min_confidence or self.MIN_CONFIDENCE
73
+
74
+ # Obtener instintos con suficiente confianza
75
+ all_instincts = self.manager.get_by_confidence(min_conf)
76
+
77
+ # Agrupar por dominio y trigger patterns
78
+ groups = self._group_instincts(all_instincts)
79
+
80
+ candidates = []
81
+
82
+ for group_key, group_instincts in groups.items():
83
+ # Verificar si es candidato a skill
84
+ if len(group_instincts) >= self.MIN_INSTINCTS_FOR_SKILL:
85
+ if self._is_skill_candidate(group_instincts):
86
+ candidates.append(self._create_skill_candidate(group_instincts))
87
+
88
+ # Verificar si es candidato a command
89
+ if len(group_instincts) >= self.MIN_INSTINCTS_FOR_COMMAND:
90
+ if self._is_command_candidate(group_instincts):
91
+ candidates.append(self._create_command_candidate(group_instincts))
92
+
93
+ # Verificar si es candidato a agent
94
+ if len(group_instincts) >= self.MIN_INSTINCTS_FOR_AGENT:
95
+ if self._is_agent_candidate(group_instincts):
96
+ candidates.append(self._create_agent_candidate(group_instincts))
97
+
98
+ # Ordenar por confianza promedio
99
+ candidates.sort(key=lambda c: c.avg_confidence, reverse=True)
100
+
101
+ return candidates
102
+
103
+ def _group_instincts(self, instincts: List[Instinct]) -> Dict[str, List[Instinct]]:
104
+ """Agrupa instintos por patrones de trigger y dominio."""
105
+ groups = defaultdict(list)
106
+
107
+ for instinct in instincts:
108
+ # Crear clave de grupo basada en palabras clave del trigger
109
+ trigger_words = set(instinct.trigger.lower().split())
110
+ for word in trigger_words:
111
+ if len(word) > 3: # Ignorar palabras cortas
112
+ groups[word].append(instinct)
113
+
114
+ # También agrupar por dominio
115
+ domain_key = f"domain:{instinct.domain.value}"
116
+ groups[domain_key].append(instinct)
117
+
118
+ # Filtrar grupos con pocos instintos
119
+ return {k: v for k, v in groups.items() if len(v) >= 2}
120
+
121
+ def _is_skill_candidate(self, instincts: List[Instinct]) -> bool:
122
+ """Determina si un grupo de instintos es candidato a skill."""
123
+ # Skills son auto-triggered behaviors
124
+ trigger_keywords = ["when", "if", "on", "always", "before", "after"]
125
+
126
+ for instinct in instincts:
127
+ if any(kw in instinct.trigger.lower() for kw in trigger_keywords):
128
+ return True
129
+
130
+ return False
131
+
132
+ def _is_command_candidate(self, instincts: List[Instinct]) -> bool:
133
+ """Determina si un grupo de instintos es candidato a command."""
134
+ # Commands son user-invoked actions
135
+ trigger_keywords = ["when user asks", "when creating", "when adding", "when running"]
136
+
137
+ for instinct in instincts:
138
+ if any(kw in instinct.trigger.lower() for kw in trigger_keywords):
139
+ return True
140
+
141
+ return False
142
+
143
+ def _is_agent_candidate(self, instincts: List[Instinct]) -> bool:
144
+ """Determina si un grupo de instintos es candidato a agent."""
145
+ # Agents son multi-step processes complejos
146
+ step_indicators = ["step", "first", "then", "finally", "check", "verify"]
147
+
148
+ # Verificar si hay secuencia de pasos
149
+ has_sequence = False
150
+ for instinct in instincts:
151
+ if any(ind in instinct.name.lower() for ind in step_indicators):
152
+ has_sequence = True
153
+ break
154
+
155
+ # Verificar si es dominio complejo
156
+ complex_domains = [
157
+ InstinctDomain.DEBUGGING,
158
+ InstinctDomain.ARCHITECTURE,
159
+ InstinctDomain.REFACTORING,
160
+ ]
161
+
162
+ has_complex_domain = any(i.domain in complex_domains for i in instincts)
163
+
164
+ return len(instincts) >= 3 and (has_sequence or has_complex_domain)
165
+
166
+ def _create_skill_candidate(self, instincts: List[Instinct]) -> EvolutionCandidate:
167
+ """Crea un candidato a skill."""
168
+ avg_conf = sum(i.confidence for i in instincts) / len(instincts)
169
+ domains = list(set(i.domain for i in instincts))
170
+
171
+ # Generar nombre
172
+ trigger_words = set()
173
+ for i in instincts:
174
+ trigger_words.update(i.trigger.lower().split())
175
+
176
+ name = "-".join(sorted(trigger_words)[:2]) or "learned-skill"
177
+
178
+ return EvolutionCandidate(
179
+ name=name,
180
+ type="skill",
181
+ instincts=instincts,
182
+ avg_confidence=avg_conf,
183
+ domains=domains,
184
+ scope=instincts[0].scope,
185
+ reason="Auto-triggered behaviors grouped by common trigger pattern",
186
+ )
187
+
188
+ def _create_command_candidate(self, instincts: List[Instinct]) -> EvolutionCandidate:
189
+ """Crea un candidato a command."""
190
+ avg_conf = sum(i.confidence for i in instincts) / len(instincts)
191
+ domains = list(set(i.domain for i in instincts))
192
+
193
+ # Generar nombre
194
+ name = instincts[0].name.split("-")[0] if instincts else "learned-command"
195
+
196
+ return EvolutionCandidate(
197
+ name=name,
198
+ type="command",
199
+ instincts=instincts,
200
+ avg_confidence=avg_conf,
201
+ domains=domains,
202
+ scope=instincts[0].scope,
203
+ reason="User-invoked actions grouped by common workflow",
204
+ )
205
+
206
+ def _create_agent_candidate(self, instincts: List[Instinct]) -> EvolutionCandidate:
207
+ """Crea un candidato a agent."""
208
+ avg_conf = sum(i.confidence for i in instincts) / len(instincts)
209
+ domains = list(set(i.domain for i in instincts))
210
+
211
+ # Generar nombre basado en dominio
212
+ domain_name = domains[0].value if domains else "general"
213
+ name = f"{domain_name}-agent"
214
+
215
+ return EvolutionCandidate(
216
+ name=name,
217
+ type="agent",
218
+ instincts=instincts,
219
+ avg_confidence=avg_conf,
220
+ domains=domains,
221
+ scope=instincts[0].scope,
222
+ reason="Multi-step process requiring isolation and depth",
223
+ )
224
+
225
+ def evolve(self, candidate: EvolutionCandidate) -> EvolvedStructure:
226
+ """
227
+ Genera la estructura evolucionada para un candidato.
228
+
229
+ Returns:
230
+ Estructura evolucionada
231
+ """
232
+ if candidate.type == "skill":
233
+ return self._generate_skill(candidate)
234
+ elif candidate.type == "command":
235
+ return self._generate_command(candidate)
236
+ elif candidate.type == "agent":
237
+ return self._generate_agent(candidate)
238
+ else:
239
+ raise ValueError(f"Unknown type: {candidate.type}")
240
+
241
+ def _generate_skill(self, candidate: EvolutionCandidate) -> EvolvedStructure:
242
+ """Genera un skill desde un candidato."""
243
+ # Agrupar triggers
244
+ triggers = set(i.trigger for i in candidate.instincts)
245
+ actions = set(i.action for i in candidate.instincts)
246
+
247
+ content = f"""# {candidate.name}
248
+
249
+ ## Description
250
+ Auto-generated skill from learned instincts.
251
+
252
+ ## Triggers
253
+ {chr(10).join(f'- {t}' for t in triggers)}
254
+
255
+ ## Actions
256
+ {chr(10).join(f'- {a}' for a in actions)}
257
+
258
+ ## Confidence
259
+ {candidate.avg_confidence:.0%}
260
+
261
+ ## Evolved From
262
+ {chr(10).join(f'- {i.name} ({i.confidence:.0%})' for i in candidate.instincts)}
263
+ """
264
+
265
+ return EvolvedStructure(
266
+ name=candidate.name,
267
+ type="skill",
268
+ description=f"Auto-triggered skill for {candidate.domains[0].value}",
269
+ instincts=[i.id for i in candidate.instincts],
270
+ content=content,
271
+ scope=candidate.scope,
272
+ )
273
+
274
+ def _generate_command(self, candidate: EvolutionCandidate) -> EvolvedStructure:
275
+ """Genera un command desde un candidato."""
276
+ steps = []
277
+ for i, instinct in enumerate(sorted(candidate.instincts,
278
+ key=lambda x: x.created_at), 1):
279
+ steps.append(f"{i}. {instinct.action}")
280
+
281
+ content = f"""# /{candidate.name}
282
+
283
+ ## Description
284
+ Auto-generated command from learned workflow.
285
+
286
+ ## Usage
287
+ `/{candidate.name}`
288
+
289
+ ## Steps
290
+ {chr(10).join(steps)}
291
+
292
+ ## Confidence
293
+ {candidate.avg_confidence:.0%}
294
+
295
+ ## Evolved From
296
+ {chr(10).join(f'- {i.name} ({i.confidence:.0%})' for i in candidate.instincts)}
297
+ """
298
+
299
+ return EvolvedStructure(
300
+ name=candidate.name,
301
+ type="command",
302
+ description=f"User-invoked command for {candidate.domains[0].value}",
303
+ instincts=[i.id for i in candidate.instincts],
304
+ content=content,
305
+ scope=candidate.scope,
306
+ )
307
+
308
+ def _generate_agent(self, candidate: EvolutionCandidate) -> EvolvedStructure:
309
+ """Genera un agent desde un candidato."""
310
+ steps = []
311
+ for i, instinct in enumerate(sorted(candidate.instincts,
312
+ key=lambda x: x.created_at), 1):
313
+ steps.append(f"{i}. {instinct.trigger}: {instinct.action}")
314
+
315
+ model = "sonnet" if candidate.avg_confidence < 0.85 else "opus"
316
+
317
+ content = f"""# {candidate.name}
318
+
319
+ ## Description
320
+ Auto-generated agent from learned multi-step process.
321
+
322
+ ## Model
323
+ {model}
324
+
325
+ ## Triggers
326
+ {chr(10).join(f'- {i.trigger}' for i in candidate.instincts[:3])}
327
+
328
+ ## Process
329
+ {chr(10).join(steps)}
330
+
331
+ ## Confidence
332
+ {candidate.avg_confidence:.0%}
333
+
334
+ ## Evolved From
335
+ {chr(10).join(f'- {i.name} ({i.confidence:.0%})' for i in candidate.instincts)}
336
+ """
337
+
338
+ return EvolvedStructure(
339
+ name=candidate.name,
340
+ type="agent",
341
+ description=f"Multi-step agent for {candidate.domains[0].value}",
342
+ instincts=[i.id for i in candidate.instincts],
343
+ content=content,
344
+ scope=candidate.scope,
345
+ )
346
+
347
+ def get_evolution_summary(self) -> Dict:
348
+ """Obtiene un resumen de posibles evoluciones."""
349
+ candidates = self.analyze()
350
+
351
+ return {
352
+ "skill_candidates": len([c for c in candidates if c.type == "skill"]),
353
+ "command_candidates": len([c for c in candidates if c.type == "command"]),
354
+ "agent_candidates": len([c for c in candidates if c.type == "agent"]),
355
+ "total_candidates": len(candidates),
356
+ "avg_confidence": sum(c.avg_confidence for c in candidates) / len(candidates) if candidates else 0,
357
+ "top_candidates": [
358
+ {"name": c.name, "type": c.type, "confidence": c.avg_confidence}
359
+ for c in candidates[:5]
360
+ ],
361
+ }