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,477 @@
1
+ """
2
+ GitHub Integration for NC1709
3
+
4
+ Provides GitHub-related functionality:
5
+ - PR creation and management
6
+ - Issue tracking
7
+ - GitHub CLI (gh) integration
8
+ - Repository information
9
+ """
10
+
11
+ import os
12
+ import subprocess
13
+ import json
14
+ from pathlib import Path
15
+ from typing import Optional, List, Dict, Any
16
+ from dataclasses import dataclass, field
17
+ from datetime import datetime
18
+
19
+
20
+ @dataclass
21
+ class PullRequest:
22
+ """Represents a GitHub Pull Request"""
23
+ number: int
24
+ title: str
25
+ url: str
26
+ state: str # open, closed, merged
27
+ author: str
28
+ branch: str
29
+ base: str
30
+ created_at: str
31
+ updated_at: str
32
+ body: Optional[str] = None
33
+ labels: List[str] = field(default_factory=list)
34
+ reviewers: List[str] = field(default_factory=list)
35
+ checks_status: Optional[str] = None
36
+
37
+
38
+ @dataclass
39
+ class Issue:
40
+ """Represents a GitHub Issue"""
41
+ number: int
42
+ title: str
43
+ url: str
44
+ state: str # open, closed
45
+ author: str
46
+ created_at: str
47
+ body: Optional[str] = None
48
+ labels: List[str] = field(default_factory=list)
49
+ assignees: List[str] = field(default_factory=list)
50
+
51
+
52
+ class GitHubIntegration:
53
+ """
54
+ GitHub integration using the gh CLI.
55
+
56
+ Features:
57
+ - Check gh CLI availability
58
+ - Create and manage PRs
59
+ - View and manage issues
60
+ - Get repository info
61
+ """
62
+
63
+ def __init__(self, repo_path: Optional[str] = None):
64
+ """
65
+ Initialize GitHub integration.
66
+
67
+ Args:
68
+ repo_path: Path to git repository (defaults to cwd)
69
+ """
70
+ self.repo_path = Path(repo_path) if repo_path else Path.cwd()
71
+ self._gh_available = self._check_gh_cli()
72
+ self._authenticated = False
73
+
74
+ if self._gh_available:
75
+ self._authenticated = self._check_auth()
76
+
77
+ def _check_gh_cli(self) -> bool:
78
+ """Check if gh CLI is available"""
79
+ try:
80
+ result = subprocess.run(
81
+ ["gh", "--version"],
82
+ capture_output=True,
83
+ text=True,
84
+ timeout=5
85
+ )
86
+ return result.returncode == 0
87
+ except (subprocess.SubprocessError, FileNotFoundError):
88
+ return False
89
+
90
+ def _check_auth(self) -> bool:
91
+ """Check if gh is authenticated"""
92
+ try:
93
+ result = subprocess.run(
94
+ ["gh", "auth", "status"],
95
+ capture_output=True,
96
+ text=True,
97
+ cwd=str(self.repo_path),
98
+ timeout=10
99
+ )
100
+ return result.returncode == 0
101
+ except subprocess.SubprocessError:
102
+ return False
103
+
104
+ @property
105
+ def is_available(self) -> bool:
106
+ """Check if GitHub integration is available"""
107
+ return self._gh_available
108
+
109
+ @property
110
+ def is_authenticated(self) -> bool:
111
+ """Check if gh is authenticated"""
112
+ return self._authenticated
113
+
114
+ def _run_gh(self, *args, json_output: bool = False) -> subprocess.CompletedProcess:
115
+ """Run a gh command"""
116
+ cmd = ["gh"] + list(args)
117
+ if json_output:
118
+ cmd.append("--json")
119
+ return subprocess.run(
120
+ cmd,
121
+ cwd=str(self.repo_path),
122
+ capture_output=True,
123
+ text=True,
124
+ timeout=60
125
+ )
126
+
127
+ def get_repo_info(self) -> Optional[Dict[str, Any]]:
128
+ """Get current repository information"""
129
+ if not self._gh_available:
130
+ return None
131
+
132
+ try:
133
+ result = self._run_gh(
134
+ "repo", "view", "--json",
135
+ "name,owner,description,url,defaultBranchRef,isPrivate"
136
+ )
137
+ if result.returncode == 0:
138
+ return json.loads(result.stdout)
139
+ except (subprocess.SubprocessError, json.JSONDecodeError):
140
+ pass
141
+ return None
142
+
143
+ def create_pr(
144
+ self,
145
+ title: str,
146
+ body: str,
147
+ base: str = "main",
148
+ draft: bool = False,
149
+ labels: Optional[List[str]] = None,
150
+ reviewers: Optional[List[str]] = None
151
+ ) -> Optional[PullRequest]:
152
+ """
153
+ Create a new Pull Request.
154
+
155
+ Args:
156
+ title: PR title
157
+ body: PR description
158
+ base: Base branch to merge into
159
+ draft: Create as draft PR
160
+ labels: List of labels to apply
161
+ reviewers: List of reviewers to request
162
+
163
+ Returns:
164
+ PullRequest object if successful, None otherwise
165
+ """
166
+ if not self._gh_available or not self._authenticated:
167
+ return None
168
+
169
+ try:
170
+ cmd = ["pr", "create", "--title", title, "--body", body, "--base", base]
171
+
172
+ if draft:
173
+ cmd.append("--draft")
174
+
175
+ if labels:
176
+ for label in labels:
177
+ cmd.extend(["--label", label])
178
+
179
+ if reviewers:
180
+ for reviewer in reviewers:
181
+ cmd.extend(["--reviewer", reviewer])
182
+
183
+ result = self._run_gh(*cmd)
184
+
185
+ if result.returncode == 0:
186
+ # Parse the output URL to get PR info
187
+ pr_url = result.stdout.strip()
188
+
189
+ # Get PR details
190
+ pr_info = self.get_pr_by_url(pr_url)
191
+ if pr_info:
192
+ return pr_info
193
+
194
+ # Fallback: create minimal PR object
195
+ return PullRequest(
196
+ number=0,
197
+ title=title,
198
+ url=pr_url,
199
+ state="open",
200
+ author="",
201
+ branch="",
202
+ base=base,
203
+ created_at=datetime.now().isoformat(),
204
+ updated_at=datetime.now().isoformat(),
205
+ body=body
206
+ )
207
+
208
+ except subprocess.SubprocessError:
209
+ pass
210
+
211
+ return None
212
+
213
+ def get_pr_by_url(self, url: str) -> Optional[PullRequest]:
214
+ """Get PR details from URL"""
215
+ try:
216
+ # Extract PR number from URL
217
+ parts = url.rstrip('/').split('/')
218
+ pr_number = parts[-1] if parts else None
219
+
220
+ if pr_number and pr_number.isdigit():
221
+ return self.get_pr(int(pr_number))
222
+ except Exception:
223
+ pass
224
+ return None
225
+
226
+ def get_pr(self, number: int) -> Optional[PullRequest]:
227
+ """Get Pull Request by number"""
228
+ if not self._gh_available:
229
+ return None
230
+
231
+ try:
232
+ result = self._run_gh(
233
+ "pr", "view", str(number), "--json",
234
+ "number,title,url,state,author,headRefName,baseRefName,createdAt,updatedAt,body,labels,reviewRequests"
235
+ )
236
+
237
+ if result.returncode == 0:
238
+ data = json.loads(result.stdout)
239
+ return PullRequest(
240
+ number=data.get("number", 0),
241
+ title=data.get("title", ""),
242
+ url=data.get("url", ""),
243
+ state=data.get("state", "").lower(),
244
+ author=data.get("author", {}).get("login", ""),
245
+ branch=data.get("headRefName", ""),
246
+ base=data.get("baseRefName", ""),
247
+ created_at=data.get("createdAt", ""),
248
+ updated_at=data.get("updatedAt", ""),
249
+ body=data.get("body"),
250
+ labels=[l.get("name", "") for l in data.get("labels", [])],
251
+ reviewers=[r.get("login", "") for r in data.get("reviewRequests", [])]
252
+ )
253
+ except (subprocess.SubprocessError, json.JSONDecodeError):
254
+ pass
255
+ return None
256
+
257
+ def list_prs(self, state: str = "open", limit: int = 10) -> List[PullRequest]:
258
+ """List Pull Requests"""
259
+ if not self._gh_available:
260
+ return []
261
+
262
+ try:
263
+ result = self._run_gh(
264
+ "pr", "list", "--state", state, "--limit", str(limit), "--json",
265
+ "number,title,url,state,author,headRefName,baseRefName,createdAt,updatedAt,labels"
266
+ )
267
+
268
+ if result.returncode == 0:
269
+ data = json.loads(result.stdout)
270
+ return [
271
+ PullRequest(
272
+ number=pr.get("number", 0),
273
+ title=pr.get("title", ""),
274
+ url=pr.get("url", ""),
275
+ state=pr.get("state", "").lower(),
276
+ author=pr.get("author", {}).get("login", ""),
277
+ branch=pr.get("headRefName", ""),
278
+ base=pr.get("baseRefName", ""),
279
+ created_at=pr.get("createdAt", ""),
280
+ updated_at=pr.get("updatedAt", ""),
281
+ labels=[l.get("name", "") for l in pr.get("labels", [])]
282
+ )
283
+ for pr in data
284
+ ]
285
+ except (subprocess.SubprocessError, json.JSONDecodeError):
286
+ pass
287
+ return []
288
+
289
+ def get_pr_checks(self, number: int) -> Optional[Dict[str, Any]]:
290
+ """Get CI check status for a PR"""
291
+ if not self._gh_available:
292
+ return None
293
+
294
+ try:
295
+ result = self._run_gh(
296
+ "pr", "checks", str(number), "--json",
297
+ "name,status,conclusion,startedAt,completedAt"
298
+ )
299
+
300
+ if result.returncode == 0:
301
+ return json.loads(result.stdout)
302
+ except (subprocess.SubprocessError, json.JSONDecodeError):
303
+ pass
304
+ return None
305
+
306
+ def merge_pr(
307
+ self,
308
+ number: int,
309
+ method: str = "merge", # merge, squash, rebase
310
+ delete_branch: bool = True
311
+ ) -> bool:
312
+ """Merge a Pull Request"""
313
+ if not self._gh_available or not self._authenticated:
314
+ return False
315
+
316
+ try:
317
+ cmd = ["pr", "merge", str(number), f"--{method}"]
318
+ if delete_branch:
319
+ cmd.append("--delete-branch")
320
+
321
+ result = self._run_gh(*cmd)
322
+ return result.returncode == 0
323
+ except subprocess.SubprocessError:
324
+ return False
325
+
326
+ def list_issues(self, state: str = "open", limit: int = 10) -> List[Issue]:
327
+ """List Issues"""
328
+ if not self._gh_available:
329
+ return []
330
+
331
+ try:
332
+ result = self._run_gh(
333
+ "issue", "list", "--state", state, "--limit", str(limit), "--json",
334
+ "number,title,url,state,author,createdAt,body,labels,assignees"
335
+ )
336
+
337
+ if result.returncode == 0:
338
+ data = json.loads(result.stdout)
339
+ return [
340
+ Issue(
341
+ number=issue.get("number", 0),
342
+ title=issue.get("title", ""),
343
+ url=issue.get("url", ""),
344
+ state=issue.get("state", "").lower(),
345
+ author=issue.get("author", {}).get("login", ""),
346
+ created_at=issue.get("createdAt", ""),
347
+ body=issue.get("body"),
348
+ labels=[l.get("name", "") for l in issue.get("labels", [])],
349
+ assignees=[a.get("login", "") for a in issue.get("assignees", [])]
350
+ )
351
+ for issue in data
352
+ ]
353
+ except (subprocess.SubprocessError, json.JSONDecodeError):
354
+ pass
355
+ return []
356
+
357
+ def create_issue(
358
+ self,
359
+ title: str,
360
+ body: str,
361
+ labels: Optional[List[str]] = None,
362
+ assignees: Optional[List[str]] = None
363
+ ) -> Optional[Issue]:
364
+ """Create a new Issue"""
365
+ if not self._gh_available or not self._authenticated:
366
+ return None
367
+
368
+ try:
369
+ cmd = ["issue", "create", "--title", title, "--body", body]
370
+
371
+ if labels:
372
+ for label in labels:
373
+ cmd.extend(["--label", label])
374
+
375
+ if assignees:
376
+ for assignee in assignees:
377
+ cmd.extend(["--assignee", assignee])
378
+
379
+ result = self._run_gh(*cmd)
380
+
381
+ if result.returncode == 0:
382
+ issue_url = result.stdout.strip()
383
+ # Extract issue number and return
384
+ parts = issue_url.rstrip('/').split('/')
385
+ issue_number = int(parts[-1]) if parts and parts[-1].isdigit() else 0
386
+
387
+ return Issue(
388
+ number=issue_number,
389
+ title=title,
390
+ url=issue_url,
391
+ state="open",
392
+ author="",
393
+ created_at=datetime.now().isoformat(),
394
+ body=body,
395
+ labels=labels or [],
396
+ assignees=assignees or []
397
+ )
398
+
399
+ except subprocess.SubprocessError:
400
+ pass
401
+
402
+ return None
403
+
404
+ def get_current_branch(self) -> Optional[str]:
405
+ """Get current git branch"""
406
+ try:
407
+ result = subprocess.run(
408
+ ["git", "branch", "--show-current"],
409
+ cwd=str(self.repo_path),
410
+ capture_output=True,
411
+ text=True
412
+ )
413
+ if result.returncode == 0:
414
+ return result.stdout.strip()
415
+ except subprocess.SubprocessError:
416
+ pass
417
+ return None
418
+
419
+ def push_branch(self, set_upstream: bool = True) -> bool:
420
+ """Push current branch to remote"""
421
+ try:
422
+ branch = self.get_current_branch()
423
+ if not branch:
424
+ return False
425
+
426
+ cmd = ["git", "push"]
427
+ if set_upstream:
428
+ cmd.extend(["-u", "origin", branch])
429
+
430
+ result = subprocess.run(
431
+ cmd,
432
+ cwd=str(self.repo_path),
433
+ capture_output=True,
434
+ text=True
435
+ )
436
+ return result.returncode == 0
437
+ except subprocess.SubprocessError:
438
+ return False
439
+
440
+
441
+ # Global GitHub integration
442
+ _github_integration: Optional[GitHubIntegration] = None
443
+
444
+
445
+ def get_github_integration() -> GitHubIntegration:
446
+ """Get or create the global GitHub integration"""
447
+ global _github_integration
448
+ if _github_integration is None:
449
+ _github_integration = GitHubIntegration()
450
+ return _github_integration
451
+
452
+
453
+ def format_pr_summary(pr: PullRequest) -> str:
454
+ """Format a PR for display"""
455
+ lines = []
456
+ lines.append(f"\033[1m#{pr.number}: {pr.title}\033[0m")
457
+ lines.append(f" URL: {pr.url}")
458
+ lines.append(f" State: {pr.state}")
459
+ lines.append(f" Branch: {pr.branch} → {pr.base}")
460
+ lines.append(f" Author: {pr.author}")
461
+ if pr.labels:
462
+ lines.append(f" Labels: {', '.join(pr.labels)}")
463
+ return "\n".join(lines)
464
+
465
+
466
+ def format_issue_summary(issue: Issue) -> str:
467
+ """Format an issue for display"""
468
+ lines = []
469
+ lines.append(f"\033[1m#{issue.number}: {issue.title}\033[0m")
470
+ lines.append(f" URL: {issue.url}")
471
+ lines.append(f" State: {issue.state}")
472
+ lines.append(f" Author: {issue.author}")
473
+ if issue.labels:
474
+ lines.append(f" Labels: {', '.join(issue.labels)}")
475
+ if issue.assignees:
476
+ lines.append(f" Assignees: {', '.join(issue.assignees)}")
477
+ return "\n".join(lines)