nc1709 1.15.4__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.
- nc1709/__init__.py +13 -0
- nc1709/agent/__init__.py +36 -0
- nc1709/agent/core.py +505 -0
- nc1709/agent/mcp_bridge.py +245 -0
- nc1709/agent/permissions.py +298 -0
- nc1709/agent/tools/__init__.py +21 -0
- nc1709/agent/tools/base.py +440 -0
- nc1709/agent/tools/bash_tool.py +367 -0
- nc1709/agent/tools/file_tools.py +454 -0
- nc1709/agent/tools/notebook_tools.py +516 -0
- nc1709/agent/tools/search_tools.py +322 -0
- nc1709/agent/tools/task_tool.py +284 -0
- nc1709/agent/tools/web_tools.py +555 -0
- nc1709/agents/__init__.py +17 -0
- nc1709/agents/auto_fix.py +506 -0
- nc1709/agents/test_generator.py +507 -0
- nc1709/checkpoints.py +372 -0
- nc1709/cli.py +3380 -0
- nc1709/cli_ui.py +1080 -0
- nc1709/cognitive/__init__.py +149 -0
- nc1709/cognitive/anticipation.py +594 -0
- nc1709/cognitive/context_engine.py +1046 -0
- nc1709/cognitive/council.py +824 -0
- nc1709/cognitive/learning.py +761 -0
- nc1709/cognitive/router.py +583 -0
- nc1709/cognitive/system.py +519 -0
- nc1709/config.py +155 -0
- nc1709/custom_commands.py +300 -0
- nc1709/executor.py +333 -0
- nc1709/file_controller.py +354 -0
- nc1709/git_integration.py +308 -0
- nc1709/github_integration.py +477 -0
- nc1709/image_input.py +446 -0
- nc1709/linting.py +519 -0
- nc1709/llm_adapter.py +667 -0
- nc1709/logger.py +192 -0
- nc1709/mcp/__init__.py +18 -0
- nc1709/mcp/client.py +370 -0
- nc1709/mcp/manager.py +407 -0
- nc1709/mcp/protocol.py +210 -0
- nc1709/mcp/server.py +473 -0
- nc1709/memory/__init__.py +20 -0
- nc1709/memory/embeddings.py +325 -0
- nc1709/memory/indexer.py +474 -0
- nc1709/memory/sessions.py +432 -0
- nc1709/memory/vector_store.py +451 -0
- nc1709/models/__init__.py +86 -0
- nc1709/models/detector.py +377 -0
- nc1709/models/formats.py +315 -0
- nc1709/models/manager.py +438 -0
- nc1709/models/registry.py +497 -0
- nc1709/performance/__init__.py +343 -0
- nc1709/performance/cache.py +705 -0
- nc1709/performance/pipeline.py +611 -0
- nc1709/performance/tiering.py +543 -0
- nc1709/plan_mode.py +362 -0
- nc1709/plugins/__init__.py +17 -0
- nc1709/plugins/agents/__init__.py +18 -0
- nc1709/plugins/agents/django_agent.py +912 -0
- nc1709/plugins/agents/docker_agent.py +623 -0
- nc1709/plugins/agents/fastapi_agent.py +887 -0
- nc1709/plugins/agents/git_agent.py +731 -0
- nc1709/plugins/agents/nextjs_agent.py +867 -0
- nc1709/plugins/base.py +359 -0
- nc1709/plugins/manager.py +411 -0
- nc1709/plugins/registry.py +337 -0
- nc1709/progress.py +443 -0
- nc1709/prompts/__init__.py +22 -0
- nc1709/prompts/agent_system.py +180 -0
- nc1709/prompts/task_prompts.py +340 -0
- nc1709/prompts/unified_prompt.py +133 -0
- nc1709/reasoning_engine.py +541 -0
- nc1709/remote_client.py +266 -0
- nc1709/shell_completions.py +349 -0
- nc1709/slash_commands.py +649 -0
- nc1709/task_classifier.py +408 -0
- nc1709/version_check.py +177 -0
- nc1709/web/__init__.py +8 -0
- nc1709/web/server.py +950 -0
- nc1709/web/templates/index.html +1127 -0
- nc1709-1.15.4.dist-info/METADATA +858 -0
- nc1709-1.15.4.dist-info/RECORD +86 -0
- nc1709-1.15.4.dist-info/WHEEL +5 -0
- nc1709-1.15.4.dist-info/entry_points.txt +2 -0
- nc1709-1.15.4.dist-info/licenses/LICENSE +9 -0
- nc1709-1.15.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Plugin Registry for NC1709
|
|
3
|
+
Discovers and catalogs available plugins
|
|
4
|
+
"""
|
|
5
|
+
import importlib
|
|
6
|
+
import importlib.util
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Dict, List, Optional, Type, Set
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
|
|
12
|
+
from .base import Plugin, PluginMetadata, PluginCapability
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class PluginInfo:
|
|
17
|
+
"""Information about a registered plugin"""
|
|
18
|
+
plugin_class: Type[Plugin]
|
|
19
|
+
metadata: PluginMetadata
|
|
20
|
+
module_path: str
|
|
21
|
+
is_builtin: bool = False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class PluginRegistry:
|
|
25
|
+
"""
|
|
26
|
+
Registry for discovering and cataloging plugins.
|
|
27
|
+
|
|
28
|
+
Handles plugin discovery from:
|
|
29
|
+
- Built-in plugins (nc1709/plugins/agents/)
|
|
30
|
+
- User plugins (~/.nc1709/plugins/)
|
|
31
|
+
- Project plugins (.nc1709/plugins/)
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self):
|
|
35
|
+
"""Initialize the registry"""
|
|
36
|
+
self._plugins: Dict[str, PluginInfo] = {}
|
|
37
|
+
self._discovery_paths: List[Path] = []
|
|
38
|
+
self._capabilities_index: Dict[PluginCapability, Set[str]] = {}
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def plugins(self) -> Dict[str, PluginInfo]:
|
|
42
|
+
"""Get all registered plugins"""
|
|
43
|
+
return self._plugins.copy()
|
|
44
|
+
|
|
45
|
+
def register(
|
|
46
|
+
self,
|
|
47
|
+
plugin_class: Type[Plugin],
|
|
48
|
+
module_path: str = "",
|
|
49
|
+
is_builtin: bool = False
|
|
50
|
+
) -> bool:
|
|
51
|
+
"""Register a plugin class
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
plugin_class: The Plugin class to register
|
|
55
|
+
module_path: Path to the module file
|
|
56
|
+
is_builtin: Whether this is a built-in plugin
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
True if registered successfully
|
|
60
|
+
"""
|
|
61
|
+
try:
|
|
62
|
+
# Create temporary instance to get metadata
|
|
63
|
+
temp_instance = plugin_class.__new__(plugin_class)
|
|
64
|
+
Plugin.__init__(temp_instance, {})
|
|
65
|
+
|
|
66
|
+
# Get metadata (may need to handle abstract property)
|
|
67
|
+
if hasattr(plugin_class, 'METADATA'):
|
|
68
|
+
metadata = plugin_class.METADATA
|
|
69
|
+
else:
|
|
70
|
+
metadata = temp_instance.metadata
|
|
71
|
+
|
|
72
|
+
plugin_name = metadata.name
|
|
73
|
+
|
|
74
|
+
# Check for duplicates
|
|
75
|
+
if plugin_name in self._plugins:
|
|
76
|
+
existing = self._plugins[plugin_name]
|
|
77
|
+
# Allow override only if existing is not builtin
|
|
78
|
+
if existing.is_builtin and not is_builtin:
|
|
79
|
+
return False
|
|
80
|
+
|
|
81
|
+
# Register plugin
|
|
82
|
+
self._plugins[plugin_name] = PluginInfo(
|
|
83
|
+
plugin_class=plugin_class,
|
|
84
|
+
metadata=metadata,
|
|
85
|
+
module_path=module_path,
|
|
86
|
+
is_builtin=is_builtin
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Index by capabilities
|
|
90
|
+
for capability in metadata.capabilities:
|
|
91
|
+
if capability not in self._capabilities_index:
|
|
92
|
+
self._capabilities_index[capability] = set()
|
|
93
|
+
self._capabilities_index[capability].add(plugin_name)
|
|
94
|
+
|
|
95
|
+
return True
|
|
96
|
+
|
|
97
|
+
except Exception as e:
|
|
98
|
+
print(f"Failed to register plugin {plugin_class}: {e}")
|
|
99
|
+
return False
|
|
100
|
+
|
|
101
|
+
def unregister(self, plugin_name: str) -> bool:
|
|
102
|
+
"""Unregister a plugin
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
plugin_name: Name of plugin to unregister
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
True if unregistered
|
|
109
|
+
"""
|
|
110
|
+
if plugin_name not in self._plugins:
|
|
111
|
+
return False
|
|
112
|
+
|
|
113
|
+
info = self._plugins[plugin_name]
|
|
114
|
+
|
|
115
|
+
# Remove from capabilities index
|
|
116
|
+
for capability in info.metadata.capabilities:
|
|
117
|
+
if capability in self._capabilities_index:
|
|
118
|
+
self._capabilities_index[capability].discard(plugin_name)
|
|
119
|
+
|
|
120
|
+
del self._plugins[plugin_name]
|
|
121
|
+
return True
|
|
122
|
+
|
|
123
|
+
def get(self, plugin_name: str) -> Optional[PluginInfo]:
|
|
124
|
+
"""Get plugin info by name
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
plugin_name: Plugin name
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
PluginInfo or None
|
|
131
|
+
"""
|
|
132
|
+
return self._plugins.get(plugin_name)
|
|
133
|
+
|
|
134
|
+
def find_by_capability(self, capability: PluginCapability) -> List[PluginInfo]:
|
|
135
|
+
"""Find plugins with a specific capability
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
capability: The capability to search for
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
List of matching PluginInfo
|
|
142
|
+
"""
|
|
143
|
+
plugin_names = self._capabilities_index.get(capability, set())
|
|
144
|
+
return [self._plugins[name] for name in plugin_names if name in self._plugins]
|
|
145
|
+
|
|
146
|
+
def find_by_keyword(self, keyword: str) -> List[PluginInfo]:
|
|
147
|
+
"""Find plugins matching a keyword
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
keyword: Keyword to search
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
List of matching PluginInfo
|
|
154
|
+
"""
|
|
155
|
+
keyword_lower = keyword.lower()
|
|
156
|
+
results = []
|
|
157
|
+
|
|
158
|
+
for info in self._plugins.values():
|
|
159
|
+
# Check name
|
|
160
|
+
if keyword_lower in info.metadata.name.lower():
|
|
161
|
+
results.append(info)
|
|
162
|
+
continue
|
|
163
|
+
|
|
164
|
+
# Check description
|
|
165
|
+
if keyword_lower in info.metadata.description.lower():
|
|
166
|
+
results.append(info)
|
|
167
|
+
continue
|
|
168
|
+
|
|
169
|
+
# Check keywords
|
|
170
|
+
if any(keyword_lower in kw.lower() for kw in info.metadata.keywords):
|
|
171
|
+
results.append(info)
|
|
172
|
+
|
|
173
|
+
return results
|
|
174
|
+
|
|
175
|
+
def add_discovery_path(self, path: Path) -> None:
|
|
176
|
+
"""Add a path for plugin discovery
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
path: Directory containing plugins
|
|
180
|
+
"""
|
|
181
|
+
if path.is_dir() and path not in self._discovery_paths:
|
|
182
|
+
self._discovery_paths.append(path)
|
|
183
|
+
|
|
184
|
+
def discover(self, include_user: bool = True, include_project: bool = True) -> int:
|
|
185
|
+
"""Discover and register plugins from configured paths
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
include_user: Include user plugins (~/.nc1709/plugins)
|
|
189
|
+
include_project: Include project plugins (.nc1709/plugins)
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Number of plugins discovered
|
|
193
|
+
"""
|
|
194
|
+
count = 0
|
|
195
|
+
|
|
196
|
+
# Built-in plugins
|
|
197
|
+
builtin_path = Path(__file__).parent / "agents"
|
|
198
|
+
if builtin_path.is_dir():
|
|
199
|
+
count += self._discover_from_path(builtin_path, is_builtin=True)
|
|
200
|
+
|
|
201
|
+
# User plugins
|
|
202
|
+
if include_user:
|
|
203
|
+
user_path = Path.home() / ".nc1709" / "plugins"
|
|
204
|
+
if user_path.is_dir():
|
|
205
|
+
count += self._discover_from_path(user_path, is_builtin=False)
|
|
206
|
+
|
|
207
|
+
# Project plugins
|
|
208
|
+
if include_project:
|
|
209
|
+
project_path = Path.cwd() / ".nc1709" / "plugins"
|
|
210
|
+
if project_path.is_dir():
|
|
211
|
+
count += self._discover_from_path(project_path, is_builtin=False)
|
|
212
|
+
|
|
213
|
+
# Custom discovery paths
|
|
214
|
+
for path in self._discovery_paths:
|
|
215
|
+
count += self._discover_from_path(path, is_builtin=False)
|
|
216
|
+
|
|
217
|
+
return count
|
|
218
|
+
|
|
219
|
+
def _discover_from_path(self, path: Path, is_builtin: bool = False) -> int:
|
|
220
|
+
"""Discover plugins from a directory
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
path: Directory to scan
|
|
224
|
+
is_builtin: Whether these are built-in plugins
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Number of plugins discovered
|
|
228
|
+
"""
|
|
229
|
+
count = 0
|
|
230
|
+
|
|
231
|
+
for file_path in path.glob("*.py"):
|
|
232
|
+
if file_path.name.startswith("_"):
|
|
233
|
+
continue
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
plugin_class = self._load_plugin_from_file(file_path)
|
|
237
|
+
if plugin_class:
|
|
238
|
+
if self.register(plugin_class, str(file_path), is_builtin):
|
|
239
|
+
count += 1
|
|
240
|
+
except Exception as e:
|
|
241
|
+
print(f"Error loading plugin from {file_path}: {e}")
|
|
242
|
+
|
|
243
|
+
return count
|
|
244
|
+
|
|
245
|
+
def _load_plugin_from_file(self, file_path: Path) -> Optional[Type[Plugin]]:
|
|
246
|
+
"""Load a plugin class from a Python file
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
file_path: Path to the .py file
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
Plugin class or None
|
|
253
|
+
"""
|
|
254
|
+
module_name = f"nc1709_plugin_{file_path.stem}"
|
|
255
|
+
|
|
256
|
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
257
|
+
if spec is None or spec.loader is None:
|
|
258
|
+
return None
|
|
259
|
+
|
|
260
|
+
module = importlib.util.module_from_spec(spec)
|
|
261
|
+
sys.modules[module_name] = module
|
|
262
|
+
|
|
263
|
+
try:
|
|
264
|
+
spec.loader.exec_module(module)
|
|
265
|
+
except Exception as e:
|
|
266
|
+
del sys.modules[module_name]
|
|
267
|
+
raise e
|
|
268
|
+
|
|
269
|
+
# Find Plugin subclass in module
|
|
270
|
+
for attr_name in dir(module):
|
|
271
|
+
attr = getattr(module, attr_name)
|
|
272
|
+
if (
|
|
273
|
+
isinstance(attr, type) and
|
|
274
|
+
issubclass(attr, Plugin) and
|
|
275
|
+
attr is not Plugin and
|
|
276
|
+
not attr.__name__.startswith("_")
|
|
277
|
+
):
|
|
278
|
+
return attr
|
|
279
|
+
|
|
280
|
+
return None
|
|
281
|
+
|
|
282
|
+
def list_all(self) -> List[Dict]:
|
|
283
|
+
"""List all registered plugins
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
List of plugin summaries
|
|
287
|
+
"""
|
|
288
|
+
return [
|
|
289
|
+
{
|
|
290
|
+
"name": info.metadata.name,
|
|
291
|
+
"version": info.metadata.version,
|
|
292
|
+
"description": info.metadata.description,
|
|
293
|
+
"capabilities": [c.value for c in info.metadata.capabilities],
|
|
294
|
+
"builtin": info.is_builtin
|
|
295
|
+
}
|
|
296
|
+
for info in self._plugins.values()
|
|
297
|
+
]
|
|
298
|
+
|
|
299
|
+
def get_dependencies(self, plugin_name: str) -> List[str]:
|
|
300
|
+
"""Get plugin dependencies
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
plugin_name: Plugin name
|
|
304
|
+
|
|
305
|
+
Returns:
|
|
306
|
+
List of dependency plugin names
|
|
307
|
+
"""
|
|
308
|
+
info = self._plugins.get(plugin_name)
|
|
309
|
+
if info:
|
|
310
|
+
return info.metadata.dependencies.copy()
|
|
311
|
+
return []
|
|
312
|
+
|
|
313
|
+
def resolve_dependencies(self, plugin_name: str) -> List[str]:
|
|
314
|
+
"""Resolve plugin dependencies in load order
|
|
315
|
+
|
|
316
|
+
Args:
|
|
317
|
+
plugin_name: Plugin name
|
|
318
|
+
|
|
319
|
+
Returns:
|
|
320
|
+
List of plugins in order they should be loaded
|
|
321
|
+
"""
|
|
322
|
+
resolved = []
|
|
323
|
+
seen = set()
|
|
324
|
+
|
|
325
|
+
def resolve(name: str):
|
|
326
|
+
if name in seen:
|
|
327
|
+
return
|
|
328
|
+
seen.add(name)
|
|
329
|
+
|
|
330
|
+
deps = self.get_dependencies(name)
|
|
331
|
+
for dep in deps:
|
|
332
|
+
resolve(dep)
|
|
333
|
+
|
|
334
|
+
resolved.append(name)
|
|
335
|
+
|
|
336
|
+
resolve(plugin_name)
|
|
337
|
+
return resolved
|