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.
Files changed (86) hide show
  1. nc1709/__init__.py +13 -0
  2. nc1709/agent/__init__.py +36 -0
  3. nc1709/agent/core.py +505 -0
  4. nc1709/agent/mcp_bridge.py +245 -0
  5. nc1709/agent/permissions.py +298 -0
  6. nc1709/agent/tools/__init__.py +21 -0
  7. nc1709/agent/tools/base.py +440 -0
  8. nc1709/agent/tools/bash_tool.py +367 -0
  9. nc1709/agent/tools/file_tools.py +454 -0
  10. nc1709/agent/tools/notebook_tools.py +516 -0
  11. nc1709/agent/tools/search_tools.py +322 -0
  12. nc1709/agent/tools/task_tool.py +284 -0
  13. nc1709/agent/tools/web_tools.py +555 -0
  14. nc1709/agents/__init__.py +17 -0
  15. nc1709/agents/auto_fix.py +506 -0
  16. nc1709/agents/test_generator.py +507 -0
  17. nc1709/checkpoints.py +372 -0
  18. nc1709/cli.py +3380 -0
  19. nc1709/cli_ui.py +1080 -0
  20. nc1709/cognitive/__init__.py +149 -0
  21. nc1709/cognitive/anticipation.py +594 -0
  22. nc1709/cognitive/context_engine.py +1046 -0
  23. nc1709/cognitive/council.py +824 -0
  24. nc1709/cognitive/learning.py +761 -0
  25. nc1709/cognitive/router.py +583 -0
  26. nc1709/cognitive/system.py +519 -0
  27. nc1709/config.py +155 -0
  28. nc1709/custom_commands.py +300 -0
  29. nc1709/executor.py +333 -0
  30. nc1709/file_controller.py +354 -0
  31. nc1709/git_integration.py +308 -0
  32. nc1709/github_integration.py +477 -0
  33. nc1709/image_input.py +446 -0
  34. nc1709/linting.py +519 -0
  35. nc1709/llm_adapter.py +667 -0
  36. nc1709/logger.py +192 -0
  37. nc1709/mcp/__init__.py +18 -0
  38. nc1709/mcp/client.py +370 -0
  39. nc1709/mcp/manager.py +407 -0
  40. nc1709/mcp/protocol.py +210 -0
  41. nc1709/mcp/server.py +473 -0
  42. nc1709/memory/__init__.py +20 -0
  43. nc1709/memory/embeddings.py +325 -0
  44. nc1709/memory/indexer.py +474 -0
  45. nc1709/memory/sessions.py +432 -0
  46. nc1709/memory/vector_store.py +451 -0
  47. nc1709/models/__init__.py +86 -0
  48. nc1709/models/detector.py +377 -0
  49. nc1709/models/formats.py +315 -0
  50. nc1709/models/manager.py +438 -0
  51. nc1709/models/registry.py +497 -0
  52. nc1709/performance/__init__.py +343 -0
  53. nc1709/performance/cache.py +705 -0
  54. nc1709/performance/pipeline.py +611 -0
  55. nc1709/performance/tiering.py +543 -0
  56. nc1709/plan_mode.py +362 -0
  57. nc1709/plugins/__init__.py +17 -0
  58. nc1709/plugins/agents/__init__.py +18 -0
  59. nc1709/plugins/agents/django_agent.py +912 -0
  60. nc1709/plugins/agents/docker_agent.py +623 -0
  61. nc1709/plugins/agents/fastapi_agent.py +887 -0
  62. nc1709/plugins/agents/git_agent.py +731 -0
  63. nc1709/plugins/agents/nextjs_agent.py +867 -0
  64. nc1709/plugins/base.py +359 -0
  65. nc1709/plugins/manager.py +411 -0
  66. nc1709/plugins/registry.py +337 -0
  67. nc1709/progress.py +443 -0
  68. nc1709/prompts/__init__.py +22 -0
  69. nc1709/prompts/agent_system.py +180 -0
  70. nc1709/prompts/task_prompts.py +340 -0
  71. nc1709/prompts/unified_prompt.py +133 -0
  72. nc1709/reasoning_engine.py +541 -0
  73. nc1709/remote_client.py +266 -0
  74. nc1709/shell_completions.py +349 -0
  75. nc1709/slash_commands.py +649 -0
  76. nc1709/task_classifier.py +408 -0
  77. nc1709/version_check.py +177 -0
  78. nc1709/web/__init__.py +8 -0
  79. nc1709/web/server.py +950 -0
  80. nc1709/web/templates/index.html +1127 -0
  81. nc1709-1.15.4.dist-info/METADATA +858 -0
  82. nc1709-1.15.4.dist-info/RECORD +86 -0
  83. nc1709-1.15.4.dist-info/WHEEL +5 -0
  84. nc1709-1.15.4.dist-info/entry_points.txt +2 -0
  85. nc1709-1.15.4.dist-info/licenses/LICENSE +9 -0
  86. nc1709-1.15.4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,411 @@
1
+ """
2
+ Plugin Manager for NC1709
3
+ Manages plugin lifecycle and execution
4
+ """
5
+ from typing import Dict, List, Optional, Any, Tuple
6
+ from pathlib import Path
7
+
8
+ from .base import Plugin, PluginStatus, PluginCapability, ActionResult
9
+ from .registry import PluginRegistry, PluginInfo
10
+
11
+
12
+ class PluginManager:
13
+ """
14
+ Manages plugin lifecycle, configuration, and execution.
15
+
16
+ Handles:
17
+ - Plugin loading/unloading
18
+ - Configuration management
19
+ - Request routing to appropriate plugins
20
+ - Action execution with confirmation
21
+ """
22
+
23
+ def __init__(self, config: Optional[Dict[str, Any]] = None):
24
+ """Initialize the plugin manager
25
+
26
+ Args:
27
+ config: Global plugin configuration
28
+ """
29
+ self._config = config or {}
30
+ self._registry = PluginRegistry()
31
+ self._instances: Dict[str, Plugin] = {}
32
+ self._load_order: List[str] = []
33
+
34
+ @property
35
+ def registry(self) -> PluginRegistry:
36
+ """Get the plugin registry"""
37
+ return self._registry
38
+
39
+ @property
40
+ def loaded_plugins(self) -> Dict[str, Plugin]:
41
+ """Get all loaded plugin instances"""
42
+ return self._instances.copy()
43
+
44
+ def discover_plugins(self) -> int:
45
+ """Discover available plugins
46
+
47
+ Returns:
48
+ Number of plugins discovered
49
+ """
50
+ return self._registry.discover()
51
+
52
+ def load_plugin(self, plugin_name: str, config: Optional[Dict] = None) -> bool:
53
+ """Load a plugin by name
54
+
55
+ Args:
56
+ plugin_name: Name of plugin to load
57
+ config: Plugin-specific configuration
58
+
59
+ Returns:
60
+ True if loaded successfully
61
+ """
62
+ # Check if already loaded
63
+ if plugin_name in self._instances:
64
+ instance = self._instances[plugin_name]
65
+ if instance.status == PluginStatus.ACTIVE:
66
+ return True
67
+
68
+ # Get plugin info from registry
69
+ info = self._registry.get(plugin_name)
70
+ if info is None:
71
+ print(f"Plugin '{plugin_name}' not found in registry")
72
+ return False
73
+
74
+ # Resolve and load dependencies first
75
+ dependencies = self._registry.resolve_dependencies(plugin_name)
76
+ for dep_name in dependencies[:-1]: # Exclude the plugin itself
77
+ if dep_name not in self._instances:
78
+ if not self.load_plugin(dep_name):
79
+ print(f"Failed to load dependency: {dep_name}")
80
+ return False
81
+
82
+ # Merge configuration
83
+ plugin_config = {}
84
+ if plugin_name in self._config:
85
+ plugin_config.update(self._config[plugin_name])
86
+ if config:
87
+ plugin_config.update(config)
88
+
89
+ # Create and load instance
90
+ try:
91
+ instance = info.plugin_class(plugin_config)
92
+ if instance.load():
93
+ self._instances[plugin_name] = instance
94
+ self._load_order.append(plugin_name)
95
+ return True
96
+ else:
97
+ print(f"Plugin '{plugin_name}' failed to load: {instance.error}")
98
+ return False
99
+
100
+ except Exception as e:
101
+ print(f"Error creating plugin '{plugin_name}': {e}")
102
+ return False
103
+
104
+ def unload_plugin(self, plugin_name: str) -> bool:
105
+ """Unload a plugin
106
+
107
+ Args:
108
+ plugin_name: Name of plugin to unload
109
+
110
+ Returns:
111
+ True if unloaded
112
+ """
113
+ if plugin_name not in self._instances:
114
+ return False
115
+
116
+ # Check if other plugins depend on this one
117
+ dependents = self._find_dependents(plugin_name)
118
+ if dependents:
119
+ # Unload dependents first
120
+ for dep in dependents:
121
+ self.unload_plugin(dep)
122
+
123
+ instance = self._instances[plugin_name]
124
+ if instance.unload():
125
+ del self._instances[plugin_name]
126
+ if plugin_name in self._load_order:
127
+ self._load_order.remove(plugin_name)
128
+ return True
129
+
130
+ return False
131
+
132
+ def reload_plugin(self, plugin_name: str) -> bool:
133
+ """Reload a plugin
134
+
135
+ Args:
136
+ plugin_name: Plugin to reload
137
+
138
+ Returns:
139
+ True if reloaded
140
+ """
141
+ config = None
142
+ if plugin_name in self._instances:
143
+ config = self._instances[plugin_name].config.copy()
144
+ self.unload_plugin(plugin_name)
145
+
146
+ return self.load_plugin(plugin_name, config)
147
+
148
+ def load_all(self, enabled_only: bool = True) -> int:
149
+ """Load all registered plugins
150
+
151
+ Args:
152
+ enabled_only: Only load plugins enabled by default
153
+
154
+ Returns:
155
+ Number of plugins loaded
156
+ """
157
+ count = 0
158
+
159
+ for name, info in self._registry.plugins.items():
160
+ if enabled_only and not info.metadata.enabled_by_default:
161
+ continue
162
+
163
+ if self.load_plugin(name):
164
+ count += 1
165
+
166
+ return count
167
+
168
+ def unload_all(self) -> None:
169
+ """Unload all plugins"""
170
+ # Unload in reverse order
171
+ for name in reversed(self._load_order.copy()):
172
+ self.unload_plugin(name)
173
+
174
+ def get_plugin(self, plugin_name: str) -> Optional[Plugin]:
175
+ """Get a loaded plugin instance
176
+
177
+ Args:
178
+ plugin_name: Plugin name
179
+
180
+ Returns:
181
+ Plugin instance or None
182
+ """
183
+ return self._instances.get(plugin_name)
184
+
185
+ def execute_action(
186
+ self,
187
+ plugin_name: str,
188
+ action_name: str,
189
+ confirm_dangerous: bool = True,
190
+ **kwargs
191
+ ) -> ActionResult:
192
+ """Execute a plugin action
193
+
194
+ Args:
195
+ plugin_name: Plugin name
196
+ action_name: Action to execute
197
+ confirm_dangerous: Ask for confirmation on dangerous actions
198
+ **kwargs: Action parameters
199
+
200
+ Returns:
201
+ ActionResult
202
+ """
203
+ plugin = self._instances.get(plugin_name)
204
+ if plugin is None:
205
+ return ActionResult.fail(f"Plugin '{plugin_name}' not loaded")
206
+
207
+ if plugin.status != PluginStatus.ACTIVE:
208
+ return ActionResult.fail(
209
+ f"Plugin '{plugin_name}' not active (status: {plugin.status.value})"
210
+ )
211
+
212
+ action = plugin.actions.get(action_name)
213
+ if action is None:
214
+ return ActionResult.fail(
215
+ f"Action '{action_name}' not found in plugin '{plugin_name}'"
216
+ )
217
+
218
+ # Check for dangerous action
219
+ if confirm_dangerous and action.dangerous:
220
+ # In a real implementation, this would prompt the user
221
+ # For now, we'll just note it in the result
222
+ pass
223
+
224
+ return plugin.execute(action_name, **kwargs)
225
+
226
+ def find_handler(self, request: str) -> List[Tuple[str, float]]:
227
+ """Find plugins that can handle a request
228
+
229
+ Args:
230
+ request: User's natural language request
231
+
232
+ Returns:
233
+ List of (plugin_name, confidence) tuples, sorted by confidence
234
+ """
235
+ candidates = []
236
+
237
+ for name, plugin in self._instances.items():
238
+ if plugin.status != PluginStatus.ACTIVE:
239
+ continue
240
+
241
+ confidence = plugin.can_handle(request)
242
+ if confidence > 0:
243
+ candidates.append((name, confidence))
244
+
245
+ # Sort by confidence (highest first)
246
+ candidates.sort(key=lambda x: x[1], reverse=True)
247
+ return candidates
248
+
249
+ def route_request(self, request: str, **kwargs) -> Optional[ActionResult]:
250
+ """Route a request to the most suitable plugin
251
+
252
+ Args:
253
+ request: User's natural language request
254
+ **kwargs: Additional parameters
255
+
256
+ Returns:
257
+ ActionResult or None if no handler found
258
+ """
259
+ handlers = self.find_handler(request)
260
+
261
+ if not handlers:
262
+ return None
263
+
264
+ # Try handlers in order of confidence
265
+ for plugin_name, confidence in handlers:
266
+ plugin = self._instances[plugin_name]
267
+
268
+ # Let plugin decide which action to use
269
+ if hasattr(plugin, 'handle_request'):
270
+ result = plugin.handle_request(request, **kwargs)
271
+ if result and result.success:
272
+ return result
273
+
274
+ return None
275
+
276
+ def get_all_actions(self) -> Dict[str, List[Dict]]:
277
+ """Get all available actions from loaded plugins
278
+
279
+ Returns:
280
+ Dict mapping plugin names to their actions
281
+ """
282
+ actions = {}
283
+
284
+ for name, plugin in self._instances.items():
285
+ if plugin.status != PluginStatus.ACTIVE:
286
+ continue
287
+
288
+ plugin_actions = []
289
+ for action_name, action in plugin.actions.items():
290
+ plugin_actions.append({
291
+ "name": action_name,
292
+ "description": action.description,
293
+ "parameters": action.parameters,
294
+ "dangerous": action.dangerous,
295
+ "requires_confirmation": action.requires_confirmation
296
+ })
297
+
298
+ actions[name] = plugin_actions
299
+
300
+ return actions
301
+
302
+ def find_by_capability(
303
+ self,
304
+ capability: PluginCapability,
305
+ loaded_only: bool = True
306
+ ) -> List[str]:
307
+ """Find plugins with a specific capability
308
+
309
+ Args:
310
+ capability: Capability to search for
311
+ loaded_only: Only return loaded plugins
312
+
313
+ Returns:
314
+ List of plugin names
315
+ """
316
+ infos = self._registry.find_by_capability(capability)
317
+ names = [info.metadata.name for info in infos]
318
+
319
+ if loaded_only:
320
+ names = [n for n in names if n in self._instances]
321
+
322
+ return names
323
+
324
+ def get_status(self) -> Dict[str, Dict]:
325
+ """Get status of all plugins
326
+
327
+ Returns:
328
+ Dict with plugin statuses
329
+ """
330
+ status = {}
331
+
332
+ for name, info in self._registry.plugins.items():
333
+ plugin_status = {
334
+ "registered": True,
335
+ "loaded": name in self._instances,
336
+ "status": "unloaded",
337
+ "version": info.metadata.version,
338
+ "builtin": info.is_builtin
339
+ }
340
+
341
+ if name in self._instances:
342
+ instance = self._instances[name]
343
+ plugin_status["status"] = instance.status.value
344
+ plugin_status["error"] = instance.error
345
+
346
+ status[name] = plugin_status
347
+
348
+ return status
349
+
350
+ def configure_plugin(self, plugin_name: str, config: Dict[str, Any]) -> bool:
351
+ """Update plugin configuration
352
+
353
+ Args:
354
+ plugin_name: Plugin to configure
355
+ config: New configuration values
356
+
357
+ Returns:
358
+ True if configured successfully
359
+ """
360
+ if plugin_name in self._instances:
361
+ return self._instances[plugin_name].configure(config)
362
+
363
+ # Store for later loading
364
+ if plugin_name not in self._config:
365
+ self._config[plugin_name] = {}
366
+ self._config[plugin_name].update(config)
367
+ return True
368
+
369
+ def _find_dependents(self, plugin_name: str) -> List[str]:
370
+ """Find plugins that depend on the given plugin
371
+
372
+ Args:
373
+ plugin_name: Plugin name
374
+
375
+ Returns:
376
+ List of dependent plugin names
377
+ """
378
+ dependents = []
379
+
380
+ for name in self._instances:
381
+ deps = self._registry.get_dependencies(name)
382
+ if plugin_name in deps:
383
+ dependents.append(name)
384
+
385
+ return dependents
386
+
387
+ def get_help(self, plugin_name: Optional[str] = None) -> str:
388
+ """Get help text for plugins
389
+
390
+ Args:
391
+ plugin_name: Specific plugin (None for all)
392
+
393
+ Returns:
394
+ Help text
395
+ """
396
+ if plugin_name:
397
+ plugin = self._instances.get(plugin_name)
398
+ if plugin:
399
+ return plugin.get_help()
400
+ return f"Plugin '{plugin_name}' not loaded"
401
+
402
+ # General help
403
+ lines = ["# NC1709 Plugins", ""]
404
+
405
+ for name, plugin in self._instances.items():
406
+ if plugin.status == PluginStatus.ACTIVE:
407
+ lines.append(f"## {plugin.metadata.name}")
408
+ lines.append(f"{plugin.metadata.description}")
409
+ lines.append("")
410
+
411
+ return "\n".join(lines)