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,440 @@
1
+ """
2
+ Tool Base Classes and Registry
3
+
4
+ Provides the foundation for the agentic tool system:
5
+ - Tool: Base class for all tools
6
+ - ToolResult: Standard result format
7
+ - ToolRegistry: Central tool management
8
+ - ToolPermission: Permission levels for tools
9
+ """
10
+
11
+ import json
12
+ from abc import ABC, abstractmethod
13
+ from dataclasses import dataclass, field
14
+ from enum import Enum
15
+ from typing import Any, Callable, Dict, List, Optional, Type, Union
16
+ import time
17
+
18
+
19
+ class ToolPermission(Enum):
20
+ """Permission levels for tool execution"""
21
+ AUTO = "auto" # Execute without asking
22
+ ASK = "ask" # Ask user before executing
23
+ DENY = "deny" # Never allow execution
24
+ ASK_ONCE = "ask_once" # Ask once per session, then auto
25
+
26
+
27
+ @dataclass
28
+ class ToolParameter:
29
+ """Definition of a tool parameter"""
30
+ name: str
31
+ description: str
32
+ type: str = "string" # string, integer, number, boolean, array, object
33
+ required: bool = True
34
+ default: Any = None
35
+ enum: Optional[List[str]] = None
36
+
37
+ def to_json_schema(self) -> Dict[str, Any]:
38
+ """Convert to JSON Schema format for LLM tool calling"""
39
+ schema = {
40
+ "type": self.type,
41
+ "description": self.description,
42
+ }
43
+ if self.enum:
44
+ schema["enum"] = self.enum
45
+ if self.default is not None:
46
+ schema["default"] = self.default
47
+ return schema
48
+
49
+
50
+ @dataclass
51
+ class ToolResult:
52
+ """Standard result format for tool execution"""
53
+ success: bool
54
+ output: str
55
+ error: Optional[str] = None
56
+ data: Any = None
57
+ duration_ms: float = 0
58
+ tool_name: str = ""
59
+ target: str = ""
60
+
61
+ def to_message(self) -> str:
62
+ """Convert to a message suitable for LLM context"""
63
+ if self.success:
64
+ return self.output
65
+ else:
66
+ return f"Error: {self.error or 'Unknown error'}\n{self.output}"
67
+
68
+ def __str__(self) -> str:
69
+ if self.success:
70
+ return f"✓ {self.tool_name}({self.target}) - {len(self.output)} chars"
71
+ else:
72
+ return f"✗ {self.tool_name}({self.target}) - {self.error}"
73
+
74
+
75
+ class Tool(ABC):
76
+ """
77
+ Base class for all agent tools.
78
+
79
+ To create a new tool:
80
+ 1. Inherit from Tool
81
+ 2. Set name, description, parameters
82
+ 3. Implement execute() method
83
+
84
+ Example:
85
+ class ReadTool(Tool):
86
+ name = "Read"
87
+ description = "Read contents of a file"
88
+ parameters = [
89
+ ToolParameter("file_path", "Path to file", required=True),
90
+ ToolParameter("limit", "Max lines to read", type="integer", required=False),
91
+ ]
92
+
93
+ def execute(self, file_path: str, limit: int = None) -> ToolResult:
94
+ # Implementation here
95
+ pass
96
+ """
97
+
98
+ # Class attributes to be overridden by subclasses
99
+ name: str = "BaseTool"
100
+ description: str = "Base tool class"
101
+ parameters: List[ToolParameter] = []
102
+ permission: ToolPermission = ToolPermission.ASK
103
+ category: str = "general"
104
+
105
+ def __init__(self):
106
+ """Initialize the tool"""
107
+ self._approved_once = False
108
+
109
+ @abstractmethod
110
+ def execute(self, **kwargs) -> ToolResult:
111
+ """
112
+ Execute the tool with given parameters.
113
+
114
+ Args:
115
+ **kwargs: Tool-specific parameters
116
+
117
+ Returns:
118
+ ToolResult with success status and output
119
+ """
120
+ pass
121
+
122
+ def validate_params(self, **kwargs) -> Optional[str]:
123
+ """
124
+ Validate parameters before execution.
125
+
126
+ Args:
127
+ **kwargs: Parameters to validate
128
+
129
+ Returns:
130
+ Error message if validation fails, None if OK
131
+ """
132
+ for param in self.parameters:
133
+ if param.required and param.name not in kwargs:
134
+ return f"Missing required parameter: {param.name}"
135
+
136
+ if param.name in kwargs:
137
+ value = kwargs[param.name]
138
+ # Type validation
139
+ if param.type == "integer" and not isinstance(value, int):
140
+ try:
141
+ int(value)
142
+ except (ValueError, TypeError):
143
+ return f"Parameter '{param.name}' must be an integer"
144
+ elif param.type == "number" and not isinstance(value, (int, float)):
145
+ try:
146
+ float(value)
147
+ except (ValueError, TypeError):
148
+ return f"Parameter '{param.name}' must be a number"
149
+ elif param.type == "boolean" and not isinstance(value, bool):
150
+ return f"Parameter '{param.name}' must be a boolean"
151
+
152
+ # Enum validation
153
+ if param.enum and value not in param.enum:
154
+ return f"Parameter '{param.name}' must be one of: {param.enum}"
155
+
156
+ return None
157
+
158
+ def run(self, **kwargs) -> ToolResult:
159
+ """
160
+ Run the tool with validation and timing.
161
+
162
+ Args:
163
+ **kwargs: Tool parameters
164
+
165
+ Returns:
166
+ ToolResult with timing information
167
+ """
168
+ # Validate parameters
169
+ error = self.validate_params(**kwargs)
170
+ if error:
171
+ return ToolResult(
172
+ success=False,
173
+ output="",
174
+ error=error,
175
+ tool_name=self.name,
176
+ target=self._get_target(**kwargs),
177
+ )
178
+
179
+ # Execute with timing
180
+ start_time = time.time()
181
+ try:
182
+ result = self.execute(**kwargs)
183
+ result.duration_ms = (time.time() - start_time) * 1000
184
+ result.tool_name = self.name
185
+ result.target = result.target or self._get_target(**kwargs)
186
+ return result
187
+ except Exception as e:
188
+ return ToolResult(
189
+ success=False,
190
+ output="",
191
+ error=str(e),
192
+ duration_ms=(time.time() - start_time) * 1000,
193
+ tool_name=self.name,
194
+ target=self._get_target(**kwargs),
195
+ )
196
+
197
+ def _get_target(self, **kwargs) -> str:
198
+ """Get a short target description for display"""
199
+ # Use first required parameter as target
200
+ for param in self.parameters:
201
+ if param.required and param.name in kwargs:
202
+ value = str(kwargs[param.name])
203
+ if len(value) > 40:
204
+ value = value[:37] + "..."
205
+ return value
206
+ return ""
207
+
208
+ def to_function_schema(self) -> Dict[str, Any]:
209
+ """
210
+ Convert tool to OpenAI-style function schema for LLM tool calling.
211
+
212
+ Returns:
213
+ Dict suitable for LLM function calling API
214
+ """
215
+ properties = {}
216
+ required = []
217
+
218
+ for param in self.parameters:
219
+ properties[param.name] = param.to_json_schema()
220
+ if param.required:
221
+ required.append(param.name)
222
+
223
+ return {
224
+ "type": "function",
225
+ "function": {
226
+ "name": self.name,
227
+ "description": self.description,
228
+ "parameters": {
229
+ "type": "object",
230
+ "properties": properties,
231
+ "required": required,
232
+ }
233
+ }
234
+ }
235
+
236
+ def get_help(self) -> str:
237
+ """Get help text for this tool"""
238
+ lines = [
239
+ f"Tool: {self.name}",
240
+ f"Description: {self.description}",
241
+ f"Permission: {self.permission.value}",
242
+ "",
243
+ "Parameters:",
244
+ ]
245
+ for param in self.parameters:
246
+ req = "(required)" if param.required else "(optional)"
247
+ lines.append(f" {param.name} [{param.type}] {req}")
248
+ lines.append(f" {param.description}")
249
+ if param.default is not None:
250
+ lines.append(f" Default: {param.default}")
251
+ if param.enum:
252
+ lines.append(f" Options: {', '.join(param.enum)}")
253
+
254
+ return "\n".join(lines)
255
+
256
+
257
+ class ToolRegistry:
258
+ """
259
+ Central registry for all available tools.
260
+
261
+ Manages tool registration, lookup, and permission checking.
262
+ """
263
+
264
+ def __init__(self):
265
+ """Initialize the registry"""
266
+ self._tools: Dict[str, Tool] = {}
267
+ self._permission_overrides: Dict[str, ToolPermission] = {}
268
+ self._session_approvals: set = set() # Tools approved for this session
269
+
270
+ def register(self, tool: Tool) -> None:
271
+ """
272
+ Register a tool.
273
+
274
+ Args:
275
+ tool: Tool instance to register
276
+ """
277
+ self._tools[tool.name] = tool
278
+
279
+ def register_class(self, tool_class: Type[Tool]) -> None:
280
+ """
281
+ Register a tool class (will be instantiated).
282
+
283
+ Args:
284
+ tool_class: Tool class to register
285
+ """
286
+ tool = tool_class()
287
+ self.register(tool)
288
+
289
+ def unregister(self, name: str) -> bool:
290
+ """
291
+ Unregister a tool by name.
292
+
293
+ Args:
294
+ name: Tool name
295
+
296
+ Returns:
297
+ True if tool was removed, False if not found
298
+ """
299
+ if name in self._tools:
300
+ del self._tools[name]
301
+ return True
302
+ return False
303
+
304
+ def get(self, name: str) -> Optional[Tool]:
305
+ """
306
+ Get a tool by name.
307
+
308
+ Args:
309
+ name: Tool name
310
+
311
+ Returns:
312
+ Tool instance or None
313
+ """
314
+ return self._tools.get(name)
315
+
316
+ def get_all(self) -> List[Tool]:
317
+ """Get all registered tools"""
318
+ return list(self._tools.values())
319
+
320
+ def get_by_category(self, category: str) -> List[Tool]:
321
+ """Get all tools in a category"""
322
+ return [t for t in self._tools.values() if t.category == category]
323
+
324
+ def list_names(self) -> List[str]:
325
+ """Get list of all tool names"""
326
+ return list(self._tools.keys())
327
+
328
+ def set_permission(self, tool_name: str, permission: ToolPermission) -> None:
329
+ """
330
+ Override permission for a specific tool.
331
+
332
+ Args:
333
+ tool_name: Name of tool
334
+ permission: New permission level
335
+ """
336
+ self._permission_overrides[tool_name] = permission
337
+
338
+ def get_permission(self, tool_name: str) -> ToolPermission:
339
+ """
340
+ Get effective permission for a tool.
341
+
342
+ Args:
343
+ tool_name: Name of tool
344
+
345
+ Returns:
346
+ Effective permission level
347
+ """
348
+ # Check overrides first
349
+ if tool_name in self._permission_overrides:
350
+ return self._permission_overrides[tool_name]
351
+
352
+ # Check tool's default
353
+ tool = self.get(tool_name)
354
+ if tool:
355
+ return tool.permission
356
+
357
+ return ToolPermission.DENY
358
+
359
+ def needs_approval(self, tool_name: str) -> bool:
360
+ """
361
+ Check if a tool needs user approval to run.
362
+
363
+ Args:
364
+ tool_name: Name of tool
365
+
366
+ Returns:
367
+ True if approval is needed
368
+ """
369
+ permission = self.get_permission(tool_name)
370
+
371
+ if permission == ToolPermission.AUTO:
372
+ return False
373
+ elif permission == ToolPermission.DENY:
374
+ return True # Will be denied anyway
375
+ elif permission == ToolPermission.ASK_ONCE:
376
+ return tool_name not in self._session_approvals
377
+ else: # ASK
378
+ return True
379
+
380
+ def approve_for_session(self, tool_name: str) -> None:
381
+ """Mark a tool as approved for this session"""
382
+ self._session_approvals.add(tool_name)
383
+
384
+ def clear_session_approvals(self) -> None:
385
+ """Clear all session approvals"""
386
+ self._session_approvals.clear()
387
+
388
+ def get_function_schemas(self) -> List[Dict[str, Any]]:
389
+ """
390
+ Get all tools as OpenAI-style function schemas.
391
+
392
+ Returns:
393
+ List of function schemas for LLM tool calling
394
+ """
395
+ return [tool.to_function_schema() for tool in self._tools.values()]
396
+
397
+ def get_tools_prompt(self) -> str:
398
+ """
399
+ Generate a tools description for LLMs that don't support function calling.
400
+
401
+ Returns:
402
+ Text description of available tools
403
+ """
404
+ lines = ["Available tools:"]
405
+ for tool in self._tools.values():
406
+ lines.append(f"\n## {tool.name}")
407
+ lines.append(f"{tool.description}")
408
+ lines.append("Parameters:")
409
+ for param in tool.parameters:
410
+ req = "(required)" if param.required else "(optional)"
411
+ lines.append(f" - {param.name}: {param.description} {req}")
412
+
413
+ lines.append("\n\nTo use a tool, respond with:")
414
+ lines.append("```tool")
415
+ lines.append('{"tool": "ToolName", "parameters": {"param1": "value1"}}')
416
+ lines.append("```")
417
+
418
+ return "\n".join(lines)
419
+
420
+
421
+ # Global default registry
422
+ _default_registry: Optional[ToolRegistry] = None
423
+
424
+
425
+ def get_default_registry() -> ToolRegistry:
426
+ """Get or create the default tool registry"""
427
+ global _default_registry
428
+ if _default_registry is None:
429
+ _default_registry = ToolRegistry()
430
+ return _default_registry
431
+
432
+
433
+ def register_tool(tool: Tool) -> None:
434
+ """Register a tool in the default registry"""
435
+ get_default_registry().register(tool)
436
+
437
+
438
+ def register_tool_class(tool_class: Type[Tool]) -> None:
439
+ """Register a tool class in the default registry"""
440
+ get_default_registry().register_class(tool_class)