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.
- hanus/__init__.py +5 -0
- hanus/__main__.py +10 -0
- hanus/action_handlers.py +76 -0
- hanus/action_parser.py +82 -0
- hanus/agent_runner.py +1445 -0
- hanus/analysis/__init__.py +5 -0
- hanus/analysis/debt.py +702 -0
- hanus/analysis/dependencies.py +475 -0
- hanus/cache/__init__.py +5 -0
- hanus/cache/response_cache.py +560 -0
- hanus/config.py +401 -0
- hanus/connectors/__init__.py +19 -0
- hanus/connectors/base.py +114 -0
- hanus/connectors/claude_connector.py +146 -0
- hanus/connectors/gemini_connector.py +141 -0
- hanus/connectors/glm_connector.py +160 -0
- hanus/connectors/ollama_connector.py +174 -0
- hanus/connectors/openai_connector.py +122 -0
- hanus/connectors/registry.py +26 -0
- hanus/context/__init__.py +7 -0
- hanus/context/manager.py +837 -0
- hanus/context/selective.py +626 -0
- hanus/error_recovery/__init__.py +5 -0
- hanus/error_recovery/auto_fix.py +605 -0
- hanus/hooks/__init__.py +5 -0
- hanus/hooks/manager.py +247 -0
- hanus/instincts/__init__.py +44 -0
- hanus/instincts/cli.py +372 -0
- hanus/instincts/detector.py +281 -0
- hanus/instincts/evolver.py +361 -0
- hanus/instincts/manager.py +343 -0
- hanus/instincts/types.py +253 -0
- hanus/logger.py +81 -0
- hanus/memory/__init__.py +8 -0
- hanus/memory/manager.py +265 -0
- hanus/memory/types.py +119 -0
- hanus/monitor.py +341 -0
- hanus/parallel/__init__.py +5 -0
- hanus/parallel/executor.py +300 -0
- hanus/permissions.py +182 -0
- hanus/plan/__init__.py +8 -0
- hanus/plan/mode.py +267 -0
- hanus/plan/models.py +152 -0
- hanus/plugin_manager.py +754 -0
- hanus/plugin_registry.py +391 -0
- hanus/plugins/__init__.py +1 -0
- hanus/plugins/arena.py +630 -0
- hanus/plugins/code_review.py +123 -0
- hanus/plugins/cortex.py +1750 -0
- hanus/plugins/deps_check.py +27 -0
- hanus/plugins/git_ops.py +33 -0
- hanus/plugins/metasploit.py +530 -0
- hanus/plugins/notes.py +583 -0
- hanus/plugins/search_code.py +59 -0
- hanus/plugins/searchsploit.py +495 -0
- hanus/plugins/strategist.py +175 -0
- hanus/plugins/webui.py +5200 -0
- hanus/profiles.py +479 -0
- hanus/profiles_builtin/__init__.py +0 -0
- hanus/profiles_builtin/architect/profile.yaml +12 -0
- hanus/profiles_builtin/architect/system_prompt.txt +71 -0
- hanus/profiles_builtin/deep/profile.yaml +12 -0
- hanus/profiles_builtin/deep/system_prompt.txt +66 -0
- hanus/profiles_builtin/developer/__init__.py +0 -0
- hanus/profiles_builtin/developer/profile.yaml +9 -0
- hanus/profiles_builtin/developer/system_prompt.txt +176 -0
- hanus/profiles_builtin/speed/profile.yaml +12 -0
- hanus/profiles_builtin/speed/system_prompt.txt +51 -0
- hanus/project_tools.py +177 -0
- hanus/query_engine.py +1594 -0
- hanus/rules/__init__.py +237 -0
- hanus/search/__init__.py +5 -0
- hanus/search/semantic.py +596 -0
- hanus/session_manager.py +547 -0
- hanus/skill_manager.py +702 -0
- hanus/skills/__init__.py +4 -0
- hanus/subagent/__init__.py +8 -0
- hanus/subagent/agents/__init__.py +253 -0
- hanus/subagent/manager.py +309 -0
- hanus/subagent/types.py +266 -0
- hanus/suggestions/__init__.py +5 -0
- hanus/suggestions/proactive.py +451 -0
- hanus/tasks/__init__.py +8 -0
- hanus/tasks/manager.py +330 -0
- hanus/tasks/models.py +106 -0
- hanus/terminal_prompt.py +166 -0
- hanus/tools.py +1849 -0
- hanus/ui.py +939 -0
- hanuscode-1.0.0.dist-info/METADATA +1151 -0
- hanuscode-1.0.0.dist-info/RECORD +93 -0
- hanuscode-1.0.0.dist-info/WHEEL +5 -0
- hanuscode-1.0.0.dist-info/entry_points.txt +2 -0
- hanuscode-1.0.0.dist-info/top_level.txt +1 -0
hanus/hooks/manager.py
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# hanus/hooks/manager.py
|
|
2
|
+
"""
|
|
3
|
+
Sistema de hooks de ciclo de vida.
|
|
4
|
+
|
|
5
|
+
Permite ejecutar callbacks en puntos específicos del flujo de ejecución.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
import time
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import Dict, List, Optional, Any, Callable
|
|
11
|
+
from enum import Enum
|
|
12
|
+
from collections import defaultdict
|
|
13
|
+
import threading
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class HookType(Enum):
|
|
17
|
+
"""Tipos de hooks disponibles."""
|
|
18
|
+
# File operations
|
|
19
|
+
PRE_READ = "pre_read"
|
|
20
|
+
POST_READ = "post_read"
|
|
21
|
+
PRE_WRITE = "pre_write"
|
|
22
|
+
POST_WRITE = "post_write"
|
|
23
|
+
PRE_EDIT = "pre_edit"
|
|
24
|
+
POST_EDIT = "post_edit"
|
|
25
|
+
|
|
26
|
+
# Command execution
|
|
27
|
+
PRE_COMMAND = "pre_command"
|
|
28
|
+
POST_COMMAND = "post_command"
|
|
29
|
+
|
|
30
|
+
# Tool execution
|
|
31
|
+
PRE_TOOL = "pre_tool"
|
|
32
|
+
POST_TOOL = "post_tool"
|
|
33
|
+
|
|
34
|
+
# Error handling
|
|
35
|
+
ON_ERROR = "on_error"
|
|
36
|
+
|
|
37
|
+
# Session lifecycle
|
|
38
|
+
ON_SESSION_START = "on_session_start"
|
|
39
|
+
ON_SESSION_END = "on_session_end"
|
|
40
|
+
|
|
41
|
+
# Plugin lifecycle
|
|
42
|
+
ON_PLUGIN_LOAD = "on_plugin_load"
|
|
43
|
+
ON_PLUGIN_UNLOAD = "on_plugin_unload"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class HookContext:
|
|
48
|
+
"""Contexto pasado a los hooks."""
|
|
49
|
+
hook_type: HookType
|
|
50
|
+
timestamp: float
|
|
51
|
+
data: Dict[str, Any] = field(default_factory=dict)
|
|
52
|
+
result: Any = None
|
|
53
|
+
error: Optional[str] = None
|
|
54
|
+
|
|
55
|
+
def to_dict(self) -> Dict:
|
|
56
|
+
return {
|
|
57
|
+
"hook_type": self.hook_type.value,
|
|
58
|
+
"timestamp": self.timestamp,
|
|
59
|
+
"data": self.data,
|
|
60
|
+
"result": str(self.result)[:200] if self.result else None,
|
|
61
|
+
"error": self.error,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@dataclass
|
|
66
|
+
class HookRegistration:
|
|
67
|
+
"""Registro de un hook."""
|
|
68
|
+
hook_type: HookType
|
|
69
|
+
callback: Callable
|
|
70
|
+
priority: int = 0 # Lower = runs first
|
|
71
|
+
name: str = ""
|
|
72
|
+
enabled: bool = True
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class HookManager:
|
|
76
|
+
"""
|
|
77
|
+
Gestiona hooks de ciclo de vida.
|
|
78
|
+
|
|
79
|
+
Uso:
|
|
80
|
+
hooks = HookManager()
|
|
81
|
+
|
|
82
|
+
@hooks.register(HookType.PRE_WRITE)
|
|
83
|
+
def my_hook(ctx):
|
|
84
|
+
print(f"About to write: {ctx.data['path']}")
|
|
85
|
+
|
|
86
|
+
# O usando decorador
|
|
87
|
+
@hook(HookType.POST_COMMAND)
|
|
88
|
+
def log_command(ctx):
|
|
89
|
+
print(f"Command executed: {ctx.data['command']}")
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
def __init__(self):
|
|
93
|
+
self._hooks: Dict[HookType, List[HookRegistration]] = defaultdict(list)
|
|
94
|
+
self._lock = threading.RLock()
|
|
95
|
+
self._hook_counter = 0
|
|
96
|
+
|
|
97
|
+
def register(
|
|
98
|
+
self,
|
|
99
|
+
hook_type: HookType,
|
|
100
|
+
callback: Callable,
|
|
101
|
+
priority: int = 0,
|
|
102
|
+
name: str = ""
|
|
103
|
+
) -> str:
|
|
104
|
+
"""
|
|
105
|
+
Registra un hook.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
hook_type: Tipo de hook
|
|
109
|
+
callback: Función a ejecutar
|
|
110
|
+
priority: Prioridad (menor = se ejecuta antes)
|
|
111
|
+
name: Nombre opcional
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
ID del hook registrado
|
|
115
|
+
"""
|
|
116
|
+
with self._lock:
|
|
117
|
+
self._hook_counter += 1
|
|
118
|
+
hook_id = f"hook_{self._hook_counter}"
|
|
119
|
+
|
|
120
|
+
registration = HookRegistration(
|
|
121
|
+
hook_type=hook_type,
|
|
122
|
+
callback=callback,
|
|
123
|
+
priority=priority,
|
|
124
|
+
name=name or callback.__name__,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
self._hooks[hook_type].append(registration)
|
|
128
|
+
# Ordenar por prioridad
|
|
129
|
+
self._hooks[hook_type].sort(key=lambda h: h.priority)
|
|
130
|
+
|
|
131
|
+
return hook_id
|
|
132
|
+
|
|
133
|
+
def unregister(self, hook_id: str) -> bool:
|
|
134
|
+
"""Elimina un hook por ID."""
|
|
135
|
+
# Simplificado - necesitaríamos tracking de IDs
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
def trigger(
|
|
139
|
+
self,
|
|
140
|
+
hook_type: HookType,
|
|
141
|
+
data: Dict[str, Any] = None,
|
|
142
|
+
result: Any = None,
|
|
143
|
+
error: str = None
|
|
144
|
+
) -> List[Any]:
|
|
145
|
+
"""
|
|
146
|
+
Dispara todos los hooks de un tipo.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
hook_type: Tipo de hook a disparar
|
|
150
|
+
data: Datos del contexto
|
|
151
|
+
result: Resultado (si aplica)
|
|
152
|
+
error: Error (si aplica)
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Lista de resultados de los hooks
|
|
156
|
+
"""
|
|
157
|
+
context = HookContext(
|
|
158
|
+
hook_type=hook_type,
|
|
159
|
+
timestamp=time.time(),
|
|
160
|
+
data=data or {},
|
|
161
|
+
result=result,
|
|
162
|
+
error=error,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
results = []
|
|
166
|
+
|
|
167
|
+
for registration in self._hooks.get(hook_type, []):
|
|
168
|
+
if not registration.enabled:
|
|
169
|
+
continue
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
result = registration.callback(context)
|
|
173
|
+
results.append(result)
|
|
174
|
+
except Exception as e:
|
|
175
|
+
# No propagar errores de hooks
|
|
176
|
+
print(f"[HookManager] Error in hook {registration.name}: {e}")
|
|
177
|
+
|
|
178
|
+
return results
|
|
179
|
+
|
|
180
|
+
def trigger_sync(
|
|
181
|
+
self,
|
|
182
|
+
hook_type: HookType,
|
|
183
|
+
data: Dict[str, Any] = None
|
|
184
|
+
) -> None:
|
|
185
|
+
"""Dispara hooks sincrónicamente."""
|
|
186
|
+
self.trigger(hook_type, data)
|
|
187
|
+
|
|
188
|
+
def enable(self, name: str) -> None:
|
|
189
|
+
"""Habilita un hook por nombre."""
|
|
190
|
+
for hooks in self._hooks.values():
|
|
191
|
+
for h in hooks:
|
|
192
|
+
if h.name == name:
|
|
193
|
+
h.enabled = True
|
|
194
|
+
|
|
195
|
+
def disable(self, name: str) -> None:
|
|
196
|
+
"""Deshabilita un hook por nombre."""
|
|
197
|
+
for hooks in self._hooks.values():
|
|
198
|
+
for h in hooks:
|
|
199
|
+
if h.name == name:
|
|
200
|
+
h.enabled = False
|
|
201
|
+
|
|
202
|
+
def list_hooks(self) -> List[Dict]:
|
|
203
|
+
"""Lista todos los hooks registrados."""
|
|
204
|
+
result = []
|
|
205
|
+
for hook_type, registrations in self._hooks.items():
|
|
206
|
+
for r in registrations:
|
|
207
|
+
result.append({
|
|
208
|
+
"type": hook_type.value,
|
|
209
|
+
"name": r.name,
|
|
210
|
+
"priority": r.priority,
|
|
211
|
+
"enabled": r.enabled,
|
|
212
|
+
})
|
|
213
|
+
return result
|
|
214
|
+
|
|
215
|
+
def clear(self, hook_type: Optional[HookType] = None) -> None:
|
|
216
|
+
"""Limpia hooks."""
|
|
217
|
+
if hook_type:
|
|
218
|
+
self._hooks[hook_type] = []
|
|
219
|
+
else:
|
|
220
|
+
self._hooks.clear()
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def hook(hook_type: HookType, priority: int = 0):
|
|
224
|
+
"""
|
|
225
|
+
Decorador para registrar hooks fácilmente.
|
|
226
|
+
|
|
227
|
+
Uso:
|
|
228
|
+
@hook(HookType.PRE_COMMAND)
|
|
229
|
+
def my_hook(ctx):
|
|
230
|
+
print(f"Before: {ctx.data}")
|
|
231
|
+
"""
|
|
232
|
+
def decorator(func: Callable) -> Callable:
|
|
233
|
+
# Registrar cuando se define
|
|
234
|
+
get_hook_manager().register(hook_type, func, priority)
|
|
235
|
+
return func
|
|
236
|
+
return decorator
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
# Instancia global
|
|
240
|
+
_manager: Optional[HookManager] = None
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def get_hook_manager() -> HookManager:
|
|
244
|
+
global _manager
|
|
245
|
+
if _manager is None:
|
|
246
|
+
_manager = HookManager()
|
|
247
|
+
return _manager
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# hanus/instincts/__init__.py
|
|
2
|
+
"""
|
|
3
|
+
Sistema de Instintos - Continuous Learning.
|
|
4
|
+
|
|
5
|
+
Los instintos son comportamientos aprendidos automáticamente desde
|
|
6
|
+
sesiones de interacción, con scoring de confianza y scope por proyecto.
|
|
7
|
+
|
|
8
|
+
Comandos:
|
|
9
|
+
- instinct-status: Ver instintos aprendidos
|
|
10
|
+
- instinct-import: Importar instintos
|
|
11
|
+
- instinct-export: Exportar instintos
|
|
12
|
+
- evolve: Evolucionar instintos a skills/comandos/agentes
|
|
13
|
+
"""
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from hanus.instincts.types import (
|
|
17
|
+
Instinct,
|
|
18
|
+
InstinctScope,
|
|
19
|
+
InstinctStatus,
|
|
20
|
+
InstinctDomain,
|
|
21
|
+
InstinctEvidence,
|
|
22
|
+
InstinctGroup,
|
|
23
|
+
ProjectInfo,
|
|
24
|
+
)
|
|
25
|
+
from hanus.instincts.manager import (
|
|
26
|
+
InstinctManager,
|
|
27
|
+
get_instinct_manager,
|
|
28
|
+
)
|
|
29
|
+
from hanus.instincts.detector import PatternDetector
|
|
30
|
+
from hanus.instincts.evolver import InstinctEvolver
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
'Instinct',
|
|
34
|
+
'InstinctScope',
|
|
35
|
+
'InstinctStatus',
|
|
36
|
+
'InstinctDomain',
|
|
37
|
+
'InstinctEvidence',
|
|
38
|
+
'InstinctGroup',
|
|
39
|
+
'ProjectInfo',
|
|
40
|
+
'InstinctManager',
|
|
41
|
+
'get_instinct_manager',
|
|
42
|
+
'PatternDetector',
|
|
43
|
+
'InstinctEvolver',
|
|
44
|
+
]
|
hanus/instincts/cli.py
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
CLI para el sistema de instintos.
|
|
4
|
+
|
|
5
|
+
Comandos:
|
|
6
|
+
- instinct-status: Muestra instintos aprendidos
|
|
7
|
+
- instinct-import: Importa instintos desde archivo/URL
|
|
8
|
+
- instinct-export: Exporta instintos a archivo
|
|
9
|
+
- evolve: Analiza y evoluciona instintos
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
from hanus.instincts.manager import get_instinct_manager, InstinctManager
|
|
19
|
+
from hanus.instincts.evolver import InstinctEvolver
|
|
20
|
+
from hanus.instincts.types import InstinctScope, InstinctDomain
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def cmd_status(args):
|
|
24
|
+
"""Muestra el estado de los instintos."""
|
|
25
|
+
manager = get_instinct_manager()
|
|
26
|
+
manager.load_all()
|
|
27
|
+
|
|
28
|
+
stats = manager.get_stats()
|
|
29
|
+
all_instincts = manager.get_all_instincts()
|
|
30
|
+
|
|
31
|
+
# Header
|
|
32
|
+
print("=" * 60)
|
|
33
|
+
print(f" INSTINCT STATUS - {stats['total']} total")
|
|
34
|
+
print("=" * 60)
|
|
35
|
+
print()
|
|
36
|
+
print(f" Project: {stats['project_info']['name']} ({stats['project_info']['id'][:8]}...)")
|
|
37
|
+
print(f" Project instincts: {stats['project']}")
|
|
38
|
+
print(f" Global instincts: {stats['global']}")
|
|
39
|
+
print()
|
|
40
|
+
|
|
41
|
+
# Agrupar por dominio
|
|
42
|
+
by_domain = manager.group_by_domain()
|
|
43
|
+
|
|
44
|
+
if not by_domain:
|
|
45
|
+
print(" No instincts learned yet.")
|
|
46
|
+
return 0
|
|
47
|
+
|
|
48
|
+
# Mostrar instintos del proyecto primero
|
|
49
|
+
project_instincts = [i for i in all_instincts.values()
|
|
50
|
+
if i.scope == InstinctScope.PROJECT]
|
|
51
|
+
if project_instincts:
|
|
52
|
+
print(f"## PROJECT-SCOPED ({stats['project_info']['name']})")
|
|
53
|
+
_print_instincts_by_domain(project_instincts, "project")
|
|
54
|
+
print()
|
|
55
|
+
|
|
56
|
+
# Mostrar instintos globales
|
|
57
|
+
global_instincts = [i for i in all_instincts.values()
|
|
58
|
+
if i.scope == InstinctScope.GLOBAL]
|
|
59
|
+
if global_instincts:
|
|
60
|
+
print("## GLOBAL (apply to all projects)")
|
|
61
|
+
_print_instincts_by_domain(global_instincts, "global")
|
|
62
|
+
print()
|
|
63
|
+
|
|
64
|
+
return 0
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _print_instincts_by_domain(instincts: list, scope: str):
|
|
68
|
+
"""Imprime instintos agrupados por dominio."""
|
|
69
|
+
from collections import defaultdict
|
|
70
|
+
|
|
71
|
+
by_domain = defaultdict(list)
|
|
72
|
+
for i in instincts:
|
|
73
|
+
by_domain[i.domain.value].append(i)
|
|
74
|
+
|
|
75
|
+
for domain, domain_instincts in sorted(by_domain.items()):
|
|
76
|
+
print(f" ### {domain.upper()} ({len(domain_instincts)})")
|
|
77
|
+
for instinct in sorted(domain_instincts, key=lambda x: x.confidence, reverse=True):
|
|
78
|
+
confidence_bar = instinct.confidence_bar
|
|
79
|
+
print(f" {confidence_bar} {instinct.confidence:.0%} {instinct.name} [{scope}]")
|
|
80
|
+
print(f" trigger: {instinct.trigger}")
|
|
81
|
+
print()
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def cmd_import(args):
|
|
85
|
+
"""Importa instintos desde archivo o URL."""
|
|
86
|
+
manager = get_instinct_manager()
|
|
87
|
+
|
|
88
|
+
# Leer archivo o URL
|
|
89
|
+
if args.file.startswith("http"):
|
|
90
|
+
import urllib.request
|
|
91
|
+
try:
|
|
92
|
+
with urllib.request.urlopen(args.file, timeout=10) as response:
|
|
93
|
+
content = response.read().decode("utf-8")
|
|
94
|
+
except Exception as e:
|
|
95
|
+
print(f"Error fetching URL: {e}")
|
|
96
|
+
return 1
|
|
97
|
+
else:
|
|
98
|
+
file_path = Path(args.file)
|
|
99
|
+
if not file_path.exists():
|
|
100
|
+
print(f"File not found: {file_path}")
|
|
101
|
+
return 1
|
|
102
|
+
content = file_path.read_text(encoding="utf-8")
|
|
103
|
+
|
|
104
|
+
# Parsear JSON
|
|
105
|
+
try:
|
|
106
|
+
data = json.loads(content)
|
|
107
|
+
except json.JSONDecodeError as e:
|
|
108
|
+
print(f"Invalid JSON: {e}")
|
|
109
|
+
return 1
|
|
110
|
+
|
|
111
|
+
# Determinar scope
|
|
112
|
+
scope = InstinctScope.PROJECT if args.scope == "project" else InstinctScope.GLOBAL
|
|
113
|
+
|
|
114
|
+
# Importar
|
|
115
|
+
if args.dry_run:
|
|
116
|
+
print("DRY RUN - Would import:")
|
|
117
|
+
for i in data.get("instincts", []):
|
|
118
|
+
print(f" - {i.get('name', 'unknown')} ({i.get('confidence', 0):.0%})")
|
|
119
|
+
return 0
|
|
120
|
+
|
|
121
|
+
imported, messages = manager.import_instincts(
|
|
122
|
+
data,
|
|
123
|
+
scope=scope,
|
|
124
|
+
force=args.force,
|
|
125
|
+
min_confidence=args.min_confidence
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
print(f"Imported {imported} instincts:")
|
|
129
|
+
for msg in messages:
|
|
130
|
+
print(f" {msg}")
|
|
131
|
+
|
|
132
|
+
return 0 if imported > 0 else 1
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def cmd_export(args):
|
|
136
|
+
"""Exporta instintos a archivo."""
|
|
137
|
+
manager = get_instinct_manager()
|
|
138
|
+
|
|
139
|
+
# Determinar scope
|
|
140
|
+
scope = None
|
|
141
|
+
if args.scope == "global":
|
|
142
|
+
scope = InstinctScope.GLOBAL
|
|
143
|
+
elif args.scope == "project":
|
|
144
|
+
scope = InstinctScope.PROJECT
|
|
145
|
+
|
|
146
|
+
# Filtrar dominios
|
|
147
|
+
domains = None
|
|
148
|
+
if args.domains:
|
|
149
|
+
domains = [InstinctDomain(d) for d in args.domains.split(",")]
|
|
150
|
+
|
|
151
|
+
# Exportar
|
|
152
|
+
data = manager.export_instincts(
|
|
153
|
+
scope=scope,
|
|
154
|
+
min_confidence=args.min_confidence,
|
|
155
|
+
domains=domains
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Escribir archivo
|
|
159
|
+
output_path = Path(args.output) if args.output else Path("instincts.json")
|
|
160
|
+
output_path.write_text(
|
|
161
|
+
json.dumps(data, indent=2, ensure_ascii=False),
|
|
162
|
+
encoding="utf-8"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
print(f"Exported {len(data['instincts'])} instincts to {output_path}")
|
|
166
|
+
return 0
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def cmd_evolve(args):
|
|
170
|
+
"""Analiza instintos y sugiere evoluciones."""
|
|
171
|
+
manager = get_instinct_manager()
|
|
172
|
+
manager.load_all()
|
|
173
|
+
|
|
174
|
+
evolver = InstinctEvolver(manager)
|
|
175
|
+
candidates = evolver.analyze(min_confidence=args.min_confidence)
|
|
176
|
+
|
|
177
|
+
stats = manager.get_stats()
|
|
178
|
+
|
|
179
|
+
# Header
|
|
180
|
+
print("=" * 60)
|
|
181
|
+
print(f" EVOLVE ANALYSIS - {stats['total']} instincts")
|
|
182
|
+
print(f" Project: {stats['project_info']['name']} ({stats['project_info']['id'][:8]}...)")
|
|
183
|
+
print(f" Project-scoped: {stats['project']} | Global: {stats['global']}")
|
|
184
|
+
print("=" * 60)
|
|
185
|
+
print()
|
|
186
|
+
|
|
187
|
+
high_conf = [c for c in candidates if c.avg_confidence >= 0.8]
|
|
188
|
+
print(f"High confidence instincts (>=80%): {len(high_conf)}")
|
|
189
|
+
print()
|
|
190
|
+
|
|
191
|
+
# Mostrar candidatos por tipo
|
|
192
|
+
skill_candidates = [c for c in candidates if c.type == "skill"]
|
|
193
|
+
command_candidates = [c for c in candidates if c.type == "command"]
|
|
194
|
+
agent_candidates = [c for c in candidates if c.type == "agent"]
|
|
195
|
+
|
|
196
|
+
if skill_candidates:
|
|
197
|
+
print("## SKILL CANDIDATES")
|
|
198
|
+
for i, c in enumerate(skill_candidates[:5], 1):
|
|
199
|
+
print(f" {i}. Cluster: \"{c.name}\"")
|
|
200
|
+
print(f" Instincts: {len(c.instincts)}")
|
|
201
|
+
print(f" Avg confidence: {c.avg_confidence:.0%}")
|
|
202
|
+
print(f" Domains: {', '.join(d.value for d in c.domains)}")
|
|
203
|
+
print(f" Reason: {c.reason}")
|
|
204
|
+
print()
|
|
205
|
+
|
|
206
|
+
if command_candidates:
|
|
207
|
+
print("## COMMAND CANDIDATES")
|
|
208
|
+
for i, c in enumerate(command_candidates[:5], 1):
|
|
209
|
+
print(f" /{c.name}")
|
|
210
|
+
print(f" From: {', '.join(i.name for i in c.instincts[:3])}")
|
|
211
|
+
print(f" Confidence: {c.avg_confidence:.0%}")
|
|
212
|
+
print()
|
|
213
|
+
|
|
214
|
+
if agent_candidates:
|
|
215
|
+
print("## AGENT CANDIDATES")
|
|
216
|
+
for i, c in enumerate(agent_candidates[:3], 1):
|
|
217
|
+
print(f" {c.name}-agent")
|
|
218
|
+
print(f" Covers {len(c.instincts)} instincts")
|
|
219
|
+
print(f" Avg confidence: {c.avg_confidence:.0%}")
|
|
220
|
+
print()
|
|
221
|
+
|
|
222
|
+
# Generar archivos si --generate
|
|
223
|
+
if args.generate:
|
|
224
|
+
evolved_dir = manager.project_instincts_dir / "evolved"
|
|
225
|
+
evolved_dir.mkdir(parents=True, exist_ok=True)
|
|
226
|
+
|
|
227
|
+
generated = 0
|
|
228
|
+
for candidate in candidates[:args.max_generate]:
|
|
229
|
+
structure = evolver.evolve(candidate)
|
|
230
|
+
|
|
231
|
+
# Determinar subdirectorio
|
|
232
|
+
subdir = evolved_dir / f"{structure.type}s"
|
|
233
|
+
subdir.mkdir(parents=True, exist_ok=True)
|
|
234
|
+
|
|
235
|
+
# Escribir archivo
|
|
236
|
+
file_path = subdir / f"{structure.name}.md"
|
|
237
|
+
file_path.write_text(structure.content, encoding="utf-8")
|
|
238
|
+
|
|
239
|
+
# Marcar instintos como evolucionados
|
|
240
|
+
for instinct_id in structure.instincts:
|
|
241
|
+
instinct = manager.get_instinct(instinct_id)
|
|
242
|
+
if instinct:
|
|
243
|
+
instinct.status = InstinctStatus.EVOLVED
|
|
244
|
+
instinct.evolved_to = structure.name
|
|
245
|
+
manager.update_instinct(instinct)
|
|
246
|
+
|
|
247
|
+
print(f" Generated: {structure.type}/{structure.name}.md")
|
|
248
|
+
generated += 1
|
|
249
|
+
|
|
250
|
+
print(f"\nGenerated {generated} structures in {evolved_dir}")
|
|
251
|
+
|
|
252
|
+
return 0
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def cmd_promote(args):
|
|
256
|
+
"""Promueve instintos del proyecto a global."""
|
|
257
|
+
manager = get_instinct_manager()
|
|
258
|
+
manager.load_all()
|
|
259
|
+
|
|
260
|
+
# Obtener instintos del proyecto
|
|
261
|
+
project_instincts = [i for i in manager._project_instincts.values()
|
|
262
|
+
if i.confidence >= args.min_confidence]
|
|
263
|
+
|
|
264
|
+
if not project_instincts:
|
|
265
|
+
print("No project instincts meet the confidence threshold.")
|
|
266
|
+
return 1
|
|
267
|
+
|
|
268
|
+
promoted = 0
|
|
269
|
+
for instinct in project_instincts:
|
|
270
|
+
if args.dry_run:
|
|
271
|
+
print(f"Would promote: {instinct.name} ({instinct.confidence:.0%})")
|
|
272
|
+
continue
|
|
273
|
+
|
|
274
|
+
# Cambiar scope
|
|
275
|
+
instinct.scope = InstinctScope.GLOBAL
|
|
276
|
+
instinct.project_id = None
|
|
277
|
+
|
|
278
|
+
# Mover a global
|
|
279
|
+
manager._global_instincts[instinct.id] = instinct
|
|
280
|
+
del manager._project_instincts[instinct.id]
|
|
281
|
+
manager._save_instinct(instinct, manager.global_dir)
|
|
282
|
+
|
|
283
|
+
print(f"Promoted: {instinct.name}")
|
|
284
|
+
promoted += 1
|
|
285
|
+
|
|
286
|
+
if args.dry_run:
|
|
287
|
+
print(f"Would promote {len(project_instincts)} instincts")
|
|
288
|
+
else:
|
|
289
|
+
print(f"Promoted {promoted} instincts to global scope")
|
|
290
|
+
|
|
291
|
+
return 0
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def main():
|
|
295
|
+
parser = argparse.ArgumentParser(
|
|
296
|
+
description="Hanuscode Instincts CLI",
|
|
297
|
+
formatter_class=argparse.RawDescriptionHelpFormatter
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
|
301
|
+
|
|
302
|
+
# instinct-status
|
|
303
|
+
status_parser = subparsers.add_parser(
|
|
304
|
+
"status",
|
|
305
|
+
help="Show learned instincts with confidence"
|
|
306
|
+
)
|
|
307
|
+
status_parser.set_defaults(func=cmd_status)
|
|
308
|
+
|
|
309
|
+
# instinct-import
|
|
310
|
+
import_parser = subparsers.add_parser(
|
|
311
|
+
"import",
|
|
312
|
+
help="Import instincts from file or URL"
|
|
313
|
+
)
|
|
314
|
+
import_parser.add_argument("file", help="File path or URL to import")
|
|
315
|
+
import_parser.add_argument("--scope", choices=["project", "global"],
|
|
316
|
+
default="project", help="Target scope")
|
|
317
|
+
import_parser.add_argument("--dry-run", action="store_true",
|
|
318
|
+
help="Preview without importing")
|
|
319
|
+
import_parser.add_argument("--force", action="store_true",
|
|
320
|
+
help="Skip confirmation prompts")
|
|
321
|
+
import_parser.add_argument("--min-confidence", type=float, default=0.5,
|
|
322
|
+
help="Minimum confidence threshold")
|
|
323
|
+
import_parser.set_defaults(func=cmd_import)
|
|
324
|
+
|
|
325
|
+
# instinct-export
|
|
326
|
+
export_parser = subparsers.add_parser(
|
|
327
|
+
"export",
|
|
328
|
+
help="Export instincts to file"
|
|
329
|
+
)
|
|
330
|
+
export_parser.add_argument("--output", "-o", help="Output file path")
|
|
331
|
+
export_parser.add_argument("--scope", choices=["project", "global", "all"],
|
|
332
|
+
default="all", help="Scope to export")
|
|
333
|
+
export_parser.add_argument("--domains", help="Comma-separated domains")
|
|
334
|
+
export_parser.add_argument("--min-confidence", type=float, default=0.5,
|
|
335
|
+
help="Minimum confidence threshold")
|
|
336
|
+
export_parser.set_defaults(func=cmd_export)
|
|
337
|
+
|
|
338
|
+
# evolve
|
|
339
|
+
evolve_parser = subparsers.add_parser(
|
|
340
|
+
"evolve",
|
|
341
|
+
help="Analyze instincts and suggest evolutions"
|
|
342
|
+
)
|
|
343
|
+
evolve_parser.add_argument("--generate", action="store_true",
|
|
344
|
+
help="Generate evolved files")
|
|
345
|
+
evolve_parser.add_argument("--min-confidence", type=float, default=0.7,
|
|
346
|
+
help="Minimum confidence for candidates")
|
|
347
|
+
evolve_parser.add_argument("--max-generate", type=int, default=10,
|
|
348
|
+
help="Maximum structures to generate")
|
|
349
|
+
evolve_parser.set_defaults(func=cmd_evolve)
|
|
350
|
+
|
|
351
|
+
# promote
|
|
352
|
+
promote_parser = subparsers.add_parser(
|
|
353
|
+
"promote",
|
|
354
|
+
help="Promote project instincts to global scope"
|
|
355
|
+
)
|
|
356
|
+
promote_parser.add_argument("--min-confidence", type=float, default=0.8,
|
|
357
|
+
help="Minimum confidence to promote")
|
|
358
|
+
promote_parser.add_argument("--dry-run", action="store_true",
|
|
359
|
+
help="Preview without promoting")
|
|
360
|
+
promote_parser.set_defaults(func=cmd_promote)
|
|
361
|
+
|
|
362
|
+
args = parser.parse_args()
|
|
363
|
+
|
|
364
|
+
if args.command is None:
|
|
365
|
+
parser.print_help()
|
|
366
|
+
return 1
|
|
367
|
+
|
|
368
|
+
return args.func(args)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
if __name__ == "__main__":
|
|
372
|
+
sys.exit(main())
|