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
nc1709/checkpoints.py ADDED
@@ -0,0 +1,372 @@
1
+ """
2
+ Checkpoints System for NC1709
3
+
4
+ Saves file states before each change, allowing users to rewind to previous versions.
5
+ Similar to Claude Code's checkpoint system.
6
+ """
7
+
8
+ import os
9
+ import json
10
+ import shutil
11
+ import hashlib
12
+ from datetime import datetime
13
+ from pathlib import Path
14
+ from typing import Dict, List, Optional, Any
15
+ from dataclasses import dataclass, asdict
16
+
17
+
18
+ @dataclass
19
+ class FileSnapshot:
20
+ """Snapshot of a single file"""
21
+ path: str
22
+ content: str
23
+ hash: str
24
+ exists: bool
25
+
26
+ @classmethod
27
+ def from_file(cls, path: str) -> "FileSnapshot":
28
+ """Create a snapshot from an existing file"""
29
+ path_obj = Path(path)
30
+ if path_obj.exists():
31
+ try:
32
+ content = path_obj.read_text(encoding='utf-8')
33
+ file_hash = hashlib.md5(content.encode()).hexdigest()
34
+ return cls(path=str(path_obj.absolute()), content=content, hash=file_hash, exists=True)
35
+ except (UnicodeDecodeError, IOError):
36
+ # Binary file or read error - store empty
37
+ return cls(path=str(path_obj.absolute()), content="", hash="", exists=True)
38
+ else:
39
+ return cls(path=str(path_obj.absolute()), content="", hash="", exists=False)
40
+
41
+
42
+ @dataclass
43
+ class Checkpoint:
44
+ """A checkpoint containing multiple file snapshots"""
45
+ id: str
46
+ timestamp: str
47
+ description: str
48
+ tool_name: str
49
+ files: Dict[str, FileSnapshot]
50
+
51
+ def to_dict(self) -> Dict[str, Any]:
52
+ """Convert to dictionary for JSON serialization"""
53
+ return {
54
+ "id": self.id,
55
+ "timestamp": self.timestamp,
56
+ "description": self.description,
57
+ "tool_name": self.tool_name,
58
+ "files": {
59
+ path: asdict(snapshot)
60
+ for path, snapshot in self.files.items()
61
+ }
62
+ }
63
+
64
+ @classmethod
65
+ def from_dict(cls, data: Dict[str, Any]) -> "Checkpoint":
66
+ """Create from dictionary"""
67
+ files = {
68
+ path: FileSnapshot(**snapshot_data)
69
+ for path, snapshot_data in data.get("files", {}).items()
70
+ }
71
+ return cls(
72
+ id=data["id"],
73
+ timestamp=data["timestamp"],
74
+ description=data["description"],
75
+ tool_name=data.get("tool_name", "unknown"),
76
+ files=files
77
+ )
78
+
79
+
80
+ class CheckpointManager:
81
+ """
82
+ Manages checkpoints for file operations.
83
+
84
+ Creates a checkpoint before each file modification, allowing
85
+ users to rewind to any previous state.
86
+ """
87
+
88
+ def __init__(self, storage_dir: Optional[str] = None, max_checkpoints: int = 50):
89
+ """
90
+ Initialize the checkpoint manager.
91
+
92
+ Args:
93
+ storage_dir: Directory to store checkpoint data.
94
+ Defaults to ~/.nc1709/checkpoints/
95
+ max_checkpoints: Maximum number of checkpoints to keep
96
+ """
97
+ if storage_dir:
98
+ self.storage_dir = Path(storage_dir)
99
+ else:
100
+ self.storage_dir = Path.home() / ".nc1709" / "checkpoints"
101
+
102
+ self.storage_dir.mkdir(parents=True, exist_ok=True)
103
+ self.max_checkpoints = max_checkpoints
104
+ self.checkpoints: List[Checkpoint] = []
105
+ self.current_index: int = -1 # Points to current checkpoint
106
+
107
+ # Load existing checkpoints for current session
108
+ self._session_file = self.storage_dir / "current_session.json"
109
+ self._load_session()
110
+
111
+ def _load_session(self) -> None:
112
+ """Load checkpoints from current session file"""
113
+ if self._session_file.exists():
114
+ try:
115
+ data = json.loads(self._session_file.read_text())
116
+ self.checkpoints = [
117
+ Checkpoint.from_dict(cp) for cp in data.get("checkpoints", [])
118
+ ]
119
+ self.current_index = data.get("current_index", len(self.checkpoints) - 1)
120
+ except (json.JSONDecodeError, KeyError):
121
+ self.checkpoints = []
122
+ self.current_index = -1
123
+
124
+ def _save_session(self) -> None:
125
+ """Save checkpoints to session file"""
126
+ data = {
127
+ "checkpoints": [cp.to_dict() for cp in self.checkpoints],
128
+ "current_index": self.current_index
129
+ }
130
+ self._session_file.write_text(json.dumps(data, indent=2))
131
+
132
+ def _generate_id(self) -> str:
133
+ """Generate a unique checkpoint ID"""
134
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
135
+ return f"cp_{timestamp}"
136
+
137
+ def create_checkpoint(
138
+ self,
139
+ files: List[str],
140
+ tool_name: str = "unknown",
141
+ description: str = ""
142
+ ) -> Checkpoint:
143
+ """
144
+ Create a checkpoint before modifying files.
145
+
146
+ Args:
147
+ files: List of file paths that will be modified
148
+ tool_name: Name of the tool making the modification
149
+ description: Human-readable description of the change
150
+
151
+ Returns:
152
+ The created Checkpoint
153
+ """
154
+ # Snapshot all files
155
+ file_snapshots = {}
156
+ for file_path in files:
157
+ snapshot = FileSnapshot.from_file(file_path)
158
+ file_snapshots[snapshot.path] = snapshot
159
+
160
+ # Create checkpoint
161
+ checkpoint = Checkpoint(
162
+ id=self._generate_id(),
163
+ timestamp=datetime.now().isoformat(),
164
+ description=description or f"{tool_name} operation",
165
+ tool_name=tool_name,
166
+ files=file_snapshots
167
+ )
168
+
169
+ # If we're not at the end (user rewound), truncate future checkpoints
170
+ if self.current_index < len(self.checkpoints) - 1:
171
+ self.checkpoints = self.checkpoints[:self.current_index + 1]
172
+
173
+ # Add checkpoint
174
+ self.checkpoints.append(checkpoint)
175
+ self.current_index = len(self.checkpoints) - 1
176
+
177
+ # Prune old checkpoints if needed
178
+ if len(self.checkpoints) > self.max_checkpoints:
179
+ self.checkpoints = self.checkpoints[-self.max_checkpoints:]
180
+ self.current_index = len(self.checkpoints) - 1
181
+
182
+ self._save_session()
183
+ return checkpoint
184
+
185
+ def rewind(self, steps: int = 1) -> Optional[Checkpoint]:
186
+ """
187
+ Rewind to a previous checkpoint.
188
+
189
+ Args:
190
+ steps: Number of checkpoints to go back (default: 1)
191
+
192
+ Returns:
193
+ The checkpoint that was restored, or None if can't rewind
194
+ """
195
+ target_index = self.current_index - steps
196
+
197
+ if target_index < 0:
198
+ return None
199
+
200
+ return self.restore_to_index(target_index)
201
+
202
+ def restore_to_index(self, index: int) -> Optional[Checkpoint]:
203
+ """
204
+ Restore files to state at specific checkpoint index.
205
+
206
+ Args:
207
+ index: Checkpoint index to restore
208
+
209
+ Returns:
210
+ The checkpoint that was restored, or None if invalid
211
+ """
212
+ if index < 0 or index >= len(self.checkpoints):
213
+ return None
214
+
215
+ checkpoint = self.checkpoints[index]
216
+
217
+ # Restore all files
218
+ for path, snapshot in checkpoint.files.items():
219
+ self._restore_file(snapshot)
220
+
221
+ self.current_index = index
222
+ self._save_session()
223
+
224
+ return checkpoint
225
+
226
+ def restore_to_id(self, checkpoint_id: str) -> Optional[Checkpoint]:
227
+ """
228
+ Restore files to state at specific checkpoint ID.
229
+
230
+ Args:
231
+ checkpoint_id: ID of checkpoint to restore
232
+
233
+ Returns:
234
+ The checkpoint that was restored, or None if not found
235
+ """
236
+ for i, cp in enumerate(self.checkpoints):
237
+ if cp.id == checkpoint_id:
238
+ return self.restore_to_index(i)
239
+ return None
240
+
241
+ def _restore_file(self, snapshot: FileSnapshot) -> None:
242
+ """Restore a single file from snapshot"""
243
+ path = Path(snapshot.path)
244
+
245
+ if not snapshot.exists:
246
+ # File didn't exist at checkpoint - delete it
247
+ if path.exists():
248
+ path.unlink()
249
+ else:
250
+ # Restore file content
251
+ path.parent.mkdir(parents=True, exist_ok=True)
252
+ path.write_text(snapshot.content, encoding='utf-8')
253
+
254
+ def list_checkpoints(self, limit: int = 10) -> List[Dict[str, Any]]:
255
+ """
256
+ List recent checkpoints.
257
+
258
+ Args:
259
+ limit: Maximum number to return
260
+
261
+ Returns:
262
+ List of checkpoint summaries
263
+ """
264
+ result = []
265
+ start = max(0, len(self.checkpoints) - limit)
266
+
267
+ for i in range(start, len(self.checkpoints)):
268
+ cp = self.checkpoints[i]
269
+ result.append({
270
+ "index": i,
271
+ "id": cp.id,
272
+ "timestamp": cp.timestamp,
273
+ "description": cp.description,
274
+ "tool": cp.tool_name,
275
+ "files": list(cp.files.keys()),
276
+ "is_current": i == self.current_index
277
+ })
278
+
279
+ return result
280
+
281
+ def get_current_checkpoint(self) -> Optional[Checkpoint]:
282
+ """Get the current checkpoint"""
283
+ if 0 <= self.current_index < len(self.checkpoints):
284
+ return self.checkpoints[self.current_index]
285
+ return None
286
+
287
+ def can_rewind(self) -> bool:
288
+ """Check if we can rewind"""
289
+ return self.current_index > 0
290
+
291
+ def can_forward(self) -> bool:
292
+ """Check if we can go forward (after rewinding)"""
293
+ return self.current_index < len(self.checkpoints) - 1
294
+
295
+ def forward(self, steps: int = 1) -> Optional[Checkpoint]:
296
+ """
297
+ Go forward after rewinding.
298
+
299
+ Args:
300
+ steps: Number of checkpoints to go forward
301
+
302
+ Returns:
303
+ The checkpoint that was restored
304
+ """
305
+ target_index = self.current_index + steps
306
+
307
+ if target_index >= len(self.checkpoints):
308
+ target_index = len(self.checkpoints) - 1
309
+
310
+ return self.restore_to_index(target_index)
311
+
312
+ def clear_session(self) -> None:
313
+ """Clear all checkpoints for current session"""
314
+ self.checkpoints = []
315
+ self.current_index = -1
316
+ if self._session_file.exists():
317
+ self._session_file.unlink()
318
+
319
+ def get_diff(self, checkpoint_id: str) -> Dict[str, Any]:
320
+ """
321
+ Get diff between current state and a checkpoint.
322
+
323
+ Args:
324
+ checkpoint_id: ID of checkpoint to compare with
325
+
326
+ Returns:
327
+ Dictionary with file changes
328
+ """
329
+ for cp in self.checkpoints:
330
+ if cp.id == checkpoint_id:
331
+ changes = {}
332
+ for path, snapshot in cp.files.items():
333
+ current = FileSnapshot.from_file(path)
334
+ if current.hash != snapshot.hash:
335
+ changes[path] = {
336
+ "had_content": snapshot.exists,
337
+ "has_content": current.exists,
338
+ "changed": True
339
+ }
340
+ return {"checkpoint": checkpoint_id, "changes": changes}
341
+
342
+ return {"error": "Checkpoint not found"}
343
+
344
+
345
+ # Global checkpoint manager instance
346
+ _checkpoint_manager: Optional[CheckpointManager] = None
347
+
348
+
349
+ def get_checkpoint_manager() -> CheckpointManager:
350
+ """Get or create the global checkpoint manager"""
351
+ global _checkpoint_manager
352
+ if _checkpoint_manager is None:
353
+ _checkpoint_manager = CheckpointManager()
354
+ return _checkpoint_manager
355
+
356
+
357
+ def checkpoint_before_edit(file_path: str, tool_name: str = "Edit") -> Checkpoint:
358
+ """Convenience function to create checkpoint before editing a file"""
359
+ return get_checkpoint_manager().create_checkpoint(
360
+ files=[file_path],
361
+ tool_name=tool_name,
362
+ description=f"Before editing {Path(file_path).name}"
363
+ )
364
+
365
+
366
+ def checkpoint_before_write(file_path: str, tool_name: str = "Write") -> Checkpoint:
367
+ """Convenience function to create checkpoint before writing a file"""
368
+ return get_checkpoint_manager().create_checkpoint(
369
+ files=[file_path],
370
+ tool_name=tool_name,
371
+ description=f"Before writing {Path(file_path).name}"
372
+ )