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/linting.py ADDED
@@ -0,0 +1,519 @@
1
+ """
2
+ Linting Integration for NC1709
3
+
4
+ Provides linting capabilities:
5
+ - Auto-detect and run appropriate linters
6
+ - Parse linter output
7
+ - Integrate with AI for auto-fixing
8
+ - Support for multiple languages
9
+ """
10
+
11
+ import os
12
+ import subprocess
13
+ import json
14
+ from pathlib import Path
15
+ from typing import Optional, List, Dict, Any, Tuple
16
+ from dataclasses import dataclass, field
17
+ from enum import Enum
18
+
19
+
20
+ class LintSeverity(Enum):
21
+ """Severity level of lint issues"""
22
+ ERROR = "error"
23
+ WARNING = "warning"
24
+ INFO = "info"
25
+ HINT = "hint"
26
+
27
+
28
+ @dataclass
29
+ class LintIssue:
30
+ """A single linting issue"""
31
+ file: str
32
+ line: int
33
+ column: int
34
+ severity: LintSeverity
35
+ message: str
36
+ rule: Optional[str] = None
37
+ source: Optional[str] = None
38
+
39
+
40
+ @dataclass
41
+ class LintResult:
42
+ """Result of running a linter"""
43
+ success: bool
44
+ linter: str
45
+ issues: List[LintIssue] = field(default_factory=list)
46
+ error_count: int = 0
47
+ warning_count: int = 0
48
+ raw_output: str = ""
49
+
50
+
51
+ class LinterConfig:
52
+ """Configuration for a specific linter"""
53
+
54
+ def __init__(
55
+ self,
56
+ name: str,
57
+ command: List[str],
58
+ file_patterns: List[str],
59
+ parser: str = "default"
60
+ ):
61
+ self.name = name
62
+ self.command = command
63
+ self.file_patterns = file_patterns
64
+ self.parser = parser
65
+
66
+
67
+ # Built-in linter configurations
68
+ LINTERS = {
69
+ # Python
70
+ "ruff": LinterConfig(
71
+ name="ruff",
72
+ command=["ruff", "check", "--output-format=json"],
73
+ file_patterns=["*.py"],
74
+ parser="ruff"
75
+ ),
76
+ "flake8": LinterConfig(
77
+ name="flake8",
78
+ command=["flake8", "--format=json"],
79
+ file_patterns=["*.py"],
80
+ parser="flake8"
81
+ ),
82
+ "mypy": LinterConfig(
83
+ name="mypy",
84
+ command=["mypy", "--output=json"],
85
+ file_patterns=["*.py"],
86
+ parser="mypy"
87
+ ),
88
+ "pylint": LinterConfig(
89
+ name="pylint",
90
+ command=["pylint", "--output-format=json"],
91
+ file_patterns=["*.py"],
92
+ parser="pylint"
93
+ ),
94
+
95
+ # JavaScript/TypeScript
96
+ "eslint": LinterConfig(
97
+ name="eslint",
98
+ command=["eslint", "--format=json"],
99
+ file_patterns=["*.js", "*.jsx", "*.ts", "*.tsx"],
100
+ parser="eslint"
101
+ ),
102
+ "tsc": LinterConfig(
103
+ name="tsc",
104
+ command=["tsc", "--noEmit"],
105
+ file_patterns=["*.ts", "*.tsx"],
106
+ parser="typescript"
107
+ ),
108
+
109
+ # Go
110
+ "golint": LinterConfig(
111
+ name="golint",
112
+ command=["golangci-lint", "run", "--out-format=json"],
113
+ file_patterns=["*.go"],
114
+ parser="golangci"
115
+ ),
116
+
117
+ # Rust
118
+ "clippy": LinterConfig(
119
+ name="clippy",
120
+ command=["cargo", "clippy", "--message-format=json"],
121
+ file_patterns=["*.rs"],
122
+ parser="cargo"
123
+ ),
124
+ }
125
+
126
+
127
+ class LintingManager:
128
+ """
129
+ Manages linting operations.
130
+
131
+ Features:
132
+ - Auto-detect available linters
133
+ - Run linters on files/directories
134
+ - Parse and format output
135
+ - Support custom linter configs
136
+ """
137
+
138
+ def __init__(self, project_path: Optional[str] = None):
139
+ self.project_path = Path(project_path) if project_path else Path.cwd()
140
+ self._available_linters: Dict[str, LinterConfig] = {}
141
+ self._detect_linters()
142
+
143
+ def _detect_linters(self) -> None:
144
+ """Detect available linters"""
145
+ for name, config in LINTERS.items():
146
+ if self._is_command_available(config.command[0]):
147
+ self._available_linters[name] = config
148
+
149
+ def _is_command_available(self, command: str) -> bool:
150
+ """Check if a command is available"""
151
+ try:
152
+ result = subprocess.run(
153
+ ["which", command],
154
+ capture_output=True,
155
+ timeout=5
156
+ )
157
+ return result.returncode == 0
158
+ except (subprocess.SubprocessError, FileNotFoundError):
159
+ return False
160
+
161
+ @property
162
+ def available_linters(self) -> List[str]:
163
+ """Get list of available linters"""
164
+ return list(self._available_linters.keys())
165
+
166
+ def get_linter_for_file(self, file_path: str) -> Optional[str]:
167
+ """Get the best linter for a file type"""
168
+ path = Path(file_path)
169
+ ext = path.suffix.lower()
170
+
171
+ # Priority order for different languages
172
+ priority = {
173
+ ".py": ["ruff", "flake8", "pylint"],
174
+ ".js": ["eslint"],
175
+ ".jsx": ["eslint"],
176
+ ".ts": ["eslint", "tsc"],
177
+ ".tsx": ["eslint", "tsc"],
178
+ ".go": ["golint"],
179
+ ".rs": ["clippy"],
180
+ }
181
+
182
+ linter_priority = priority.get(ext, [])
183
+ for linter in linter_priority:
184
+ if linter in self._available_linters:
185
+ return linter
186
+
187
+ return None
188
+
189
+ def run_linter(
190
+ self,
191
+ linter_name: str,
192
+ target: Optional[str] = None,
193
+ fix: bool = False
194
+ ) -> LintResult:
195
+ """
196
+ Run a specific linter.
197
+
198
+ Args:
199
+ linter_name: Name of the linter to run
200
+ target: File or directory to lint (defaults to project root)
201
+ fix: Whether to auto-fix issues (if supported)
202
+
203
+ Returns:
204
+ LintResult with issues found
205
+ """
206
+ if linter_name not in self._available_linters:
207
+ return LintResult(
208
+ success=False,
209
+ linter=linter_name,
210
+ raw_output=f"Linter '{linter_name}' not available"
211
+ )
212
+
213
+ config = self._available_linters[linter_name]
214
+ cmd = config.command.copy()
215
+
216
+ # Add fix flag if supported
217
+ if fix:
218
+ if linter_name == "ruff":
219
+ cmd.append("--fix")
220
+ elif linter_name == "eslint":
221
+ cmd.append("--fix")
222
+
223
+ # Add target
224
+ if target:
225
+ cmd.append(target)
226
+ else:
227
+ cmd.append(str(self.project_path))
228
+
229
+ try:
230
+ result = subprocess.run(
231
+ cmd,
232
+ cwd=str(self.project_path),
233
+ capture_output=True,
234
+ text=True,
235
+ timeout=120
236
+ )
237
+
238
+ issues = self._parse_output(config.parser, result.stdout, result.stderr)
239
+
240
+ error_count = sum(1 for i in issues if i.severity == LintSeverity.ERROR)
241
+ warning_count = sum(1 for i in issues if i.severity == LintSeverity.WARNING)
242
+
243
+ return LintResult(
244
+ success=result.returncode == 0,
245
+ linter=linter_name,
246
+ issues=issues,
247
+ error_count=error_count,
248
+ warning_count=warning_count,
249
+ raw_output=result.stdout or result.stderr
250
+ )
251
+
252
+ except subprocess.TimeoutExpired:
253
+ return LintResult(
254
+ success=False,
255
+ linter=linter_name,
256
+ raw_output="Linter timed out"
257
+ )
258
+ except Exception as e:
259
+ return LintResult(
260
+ success=False,
261
+ linter=linter_name,
262
+ raw_output=str(e)
263
+ )
264
+
265
+ def _parse_output(
266
+ self,
267
+ parser: str,
268
+ stdout: str,
269
+ stderr: str
270
+ ) -> List[LintIssue]:
271
+ """Parse linter output"""
272
+ issues = []
273
+
274
+ if parser == "ruff":
275
+ issues = self._parse_ruff(stdout)
276
+ elif parser == "eslint":
277
+ issues = self._parse_eslint(stdout)
278
+ elif parser == "flake8":
279
+ issues = self._parse_flake8(stdout)
280
+ elif parser == "pylint":
281
+ issues = self._parse_pylint(stdout)
282
+ elif parser == "typescript":
283
+ issues = self._parse_typescript(stdout, stderr)
284
+ elif parser == "golangci":
285
+ issues = self._parse_golangci(stdout)
286
+ else:
287
+ # Default: try to parse as generic output
288
+ issues = self._parse_generic(stdout, stderr)
289
+
290
+ return issues
291
+
292
+ def _parse_ruff(self, output: str) -> List[LintIssue]:
293
+ """Parse ruff JSON output"""
294
+ issues = []
295
+ try:
296
+ data = json.loads(output) if output else []
297
+ for item in data:
298
+ issues.append(LintIssue(
299
+ file=item.get("filename", ""),
300
+ line=item.get("location", {}).get("row", 0),
301
+ column=item.get("location", {}).get("column", 0),
302
+ severity=LintSeverity.ERROR,
303
+ message=item.get("message", ""),
304
+ rule=item.get("code"),
305
+ source="ruff"
306
+ ))
307
+ except json.JSONDecodeError:
308
+ pass
309
+ return issues
310
+
311
+ def _parse_eslint(self, output: str) -> List[LintIssue]:
312
+ """Parse ESLint JSON output"""
313
+ issues = []
314
+ try:
315
+ data = json.loads(output) if output else []
316
+ for file_result in data:
317
+ file_path = file_result.get("filePath", "")
318
+ for msg in file_result.get("messages", []):
319
+ severity = LintSeverity.ERROR if msg.get("severity", 0) == 2 else LintSeverity.WARNING
320
+ issues.append(LintIssue(
321
+ file=file_path,
322
+ line=msg.get("line", 0),
323
+ column=msg.get("column", 0),
324
+ severity=severity,
325
+ message=msg.get("message", ""),
326
+ rule=msg.get("ruleId"),
327
+ source="eslint"
328
+ ))
329
+ except json.JSONDecodeError:
330
+ pass
331
+ return issues
332
+
333
+ def _parse_flake8(self, output: str) -> List[LintIssue]:
334
+ """Parse flake8 output"""
335
+ issues = []
336
+ # flake8 format: file:line:col: code message
337
+ for line in output.split('\n'):
338
+ if ':' in line:
339
+ parts = line.split(':', 3)
340
+ if len(parts) >= 4:
341
+ try:
342
+ issues.append(LintIssue(
343
+ file=parts[0],
344
+ line=int(parts[1]),
345
+ column=int(parts[2]),
346
+ severity=LintSeverity.ERROR,
347
+ message=parts[3].strip(),
348
+ source="flake8"
349
+ ))
350
+ except (ValueError, IndexError):
351
+ pass
352
+ return issues
353
+
354
+ def _parse_pylint(self, output: str) -> List[LintIssue]:
355
+ """Parse pylint JSON output"""
356
+ issues = []
357
+ try:
358
+ data = json.loads(output) if output else []
359
+ for item in data:
360
+ severity_map = {
361
+ "error": LintSeverity.ERROR,
362
+ "warning": LintSeverity.WARNING,
363
+ "convention": LintSeverity.INFO,
364
+ "refactor": LintSeverity.HINT,
365
+ }
366
+ issues.append(LintIssue(
367
+ file=item.get("path", ""),
368
+ line=item.get("line", 0),
369
+ column=item.get("column", 0),
370
+ severity=severity_map.get(item.get("type", "").lower(), LintSeverity.WARNING),
371
+ message=item.get("message", ""),
372
+ rule=item.get("symbol"),
373
+ source="pylint"
374
+ ))
375
+ except json.JSONDecodeError:
376
+ pass
377
+ return issues
378
+
379
+ def _parse_typescript(self, stdout: str, stderr: str) -> List[LintIssue]:
380
+ """Parse TypeScript compiler output"""
381
+ issues = []
382
+ output = stderr or stdout
383
+ # tsc format: file(line,col): error TS1234: message
384
+ for line in output.split('\n'):
385
+ if ': error ' in line or ': warning ' in line:
386
+ try:
387
+ # Parse the line
388
+ parts = line.split(':', 2)
389
+ if len(parts) >= 3:
390
+ file_loc = parts[0]
391
+ if '(' in file_loc:
392
+ file_path = file_loc[:file_loc.index('(')]
393
+ loc = file_loc[file_loc.index('(')+1:file_loc.index(')')]
394
+ line_num, col = loc.split(',')
395
+ severity = LintSeverity.ERROR if 'error' in parts[1].lower() else LintSeverity.WARNING
396
+ issues.append(LintIssue(
397
+ file=file_path,
398
+ line=int(line_num),
399
+ column=int(col),
400
+ severity=severity,
401
+ message=parts[2].strip(),
402
+ source="tsc"
403
+ ))
404
+ except (ValueError, IndexError):
405
+ pass
406
+ return issues
407
+
408
+ def _parse_golangci(self, output: str) -> List[LintIssue]:
409
+ """Parse golangci-lint JSON output"""
410
+ issues = []
411
+ try:
412
+ data = json.loads(output) if output else {}
413
+ for item in data.get("Issues", []):
414
+ issues.append(LintIssue(
415
+ file=item.get("Pos", {}).get("Filename", ""),
416
+ line=item.get("Pos", {}).get("Line", 0),
417
+ column=item.get("Pos", {}).get("Column", 0),
418
+ severity=LintSeverity.ERROR,
419
+ message=item.get("Text", ""),
420
+ rule=item.get("FromLinter"),
421
+ source="golangci-lint"
422
+ ))
423
+ except json.JSONDecodeError:
424
+ pass
425
+ return issues
426
+
427
+ def _parse_generic(self, stdout: str, stderr: str) -> List[LintIssue]:
428
+ """Generic parser for unknown output formats"""
429
+ issues = []
430
+ output = stderr or stdout
431
+
432
+ for line in output.split('\n'):
433
+ line = line.strip()
434
+ if not line or line.startswith('#'):
435
+ continue
436
+
437
+ # Try common patterns
438
+ if ':' in line:
439
+ issues.append(LintIssue(
440
+ file="",
441
+ line=0,
442
+ column=0,
443
+ severity=LintSeverity.WARNING,
444
+ message=line,
445
+ source="unknown"
446
+ ))
447
+
448
+ return issues
449
+
450
+ def lint_file(self, file_path: str, fix: bool = False) -> Optional[LintResult]:
451
+ """Lint a specific file"""
452
+ linter = self.get_linter_for_file(file_path)
453
+ if not linter:
454
+ return None
455
+ return self.run_linter(linter, file_path, fix=fix)
456
+
457
+ def lint_project(self, fix: bool = False) -> Dict[str, LintResult]:
458
+ """Lint the entire project with all available linters"""
459
+ results = {}
460
+ for linter_name in self._available_linters:
461
+ results[linter_name] = self.run_linter(linter_name, fix=fix)
462
+ return results
463
+
464
+
465
+ # Global linting manager
466
+ _linting_manager: Optional[LintingManager] = None
467
+
468
+
469
+ def get_linting_manager() -> LintingManager:
470
+ """Get or create the global linting manager"""
471
+ global _linting_manager
472
+ if _linting_manager is None:
473
+ _linting_manager = LintingManager()
474
+ return _linting_manager
475
+
476
+
477
+ def format_lint_result(result: LintResult) -> str:
478
+ """Format a lint result for display"""
479
+ lines = []
480
+
481
+ if not result.issues:
482
+ lines.append(f"\033[32m{result.linter}: No issues found\033[0m")
483
+ return "\n".join(lines)
484
+
485
+ lines.append(f"\033[1m{result.linter}: {result.error_count} errors, {result.warning_count} warnings\033[0m\n")
486
+
487
+ for issue in result.issues[:20]: # Limit to 20 issues
488
+ severity_color = {
489
+ LintSeverity.ERROR: "\033[31m",
490
+ LintSeverity.WARNING: "\033[33m",
491
+ LintSeverity.INFO: "\033[36m",
492
+ LintSeverity.HINT: "\033[90m",
493
+ }.get(issue.severity, "")
494
+
495
+ file_loc = f"{issue.file}:{issue.line}:{issue.column}" if issue.file else ""
496
+ rule_str = f" [{issue.rule}]" if issue.rule else ""
497
+
498
+ lines.append(f" {severity_color}{issue.severity.value}{rule_str}\033[0m {file_loc}")
499
+ lines.append(f" {issue.message}")
500
+
501
+ if len(result.issues) > 20:
502
+ lines.append(f"\n ... and {len(result.issues) - 20} more issues")
503
+
504
+ return "\n".join(lines)
505
+
506
+
507
+ def generate_fix_prompt(result: LintResult) -> str:
508
+ """Generate a prompt for AI to fix lint issues"""
509
+ issues_text = []
510
+
511
+ for issue in result.issues[:10]: # Limit for context
512
+ issues_text.append(f"- {issue.file}:{issue.line}: {issue.message}")
513
+
514
+ return f"""Fix the following {result.linter} issues:
515
+
516
+ {chr(10).join(issues_text)}
517
+
518
+ Please fix each issue while maintaining the existing code functionality.
519
+ """