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.
- nc1709/__init__.py +13 -0
- nc1709/agent/__init__.py +36 -0
- nc1709/agent/core.py +505 -0
- nc1709/agent/mcp_bridge.py +245 -0
- nc1709/agent/permissions.py +298 -0
- nc1709/agent/tools/__init__.py +21 -0
- nc1709/agent/tools/base.py +440 -0
- nc1709/agent/tools/bash_tool.py +367 -0
- nc1709/agent/tools/file_tools.py +454 -0
- nc1709/agent/tools/notebook_tools.py +516 -0
- nc1709/agent/tools/search_tools.py +322 -0
- nc1709/agent/tools/task_tool.py +284 -0
- nc1709/agent/tools/web_tools.py +555 -0
- nc1709/agents/__init__.py +17 -0
- nc1709/agents/auto_fix.py +506 -0
- nc1709/agents/test_generator.py +507 -0
- nc1709/checkpoints.py +372 -0
- nc1709/cli.py +3380 -0
- nc1709/cli_ui.py +1080 -0
- nc1709/cognitive/__init__.py +149 -0
- nc1709/cognitive/anticipation.py +594 -0
- nc1709/cognitive/context_engine.py +1046 -0
- nc1709/cognitive/council.py +824 -0
- nc1709/cognitive/learning.py +761 -0
- nc1709/cognitive/router.py +583 -0
- nc1709/cognitive/system.py +519 -0
- nc1709/config.py +155 -0
- nc1709/custom_commands.py +300 -0
- nc1709/executor.py +333 -0
- nc1709/file_controller.py +354 -0
- nc1709/git_integration.py +308 -0
- nc1709/github_integration.py +477 -0
- nc1709/image_input.py +446 -0
- nc1709/linting.py +519 -0
- nc1709/llm_adapter.py +667 -0
- nc1709/logger.py +192 -0
- nc1709/mcp/__init__.py +18 -0
- nc1709/mcp/client.py +370 -0
- nc1709/mcp/manager.py +407 -0
- nc1709/mcp/protocol.py +210 -0
- nc1709/mcp/server.py +473 -0
- nc1709/memory/__init__.py +20 -0
- nc1709/memory/embeddings.py +325 -0
- nc1709/memory/indexer.py +474 -0
- nc1709/memory/sessions.py +432 -0
- nc1709/memory/vector_store.py +451 -0
- nc1709/models/__init__.py +86 -0
- nc1709/models/detector.py +377 -0
- nc1709/models/formats.py +315 -0
- nc1709/models/manager.py +438 -0
- nc1709/models/registry.py +497 -0
- nc1709/performance/__init__.py +343 -0
- nc1709/performance/cache.py +705 -0
- nc1709/performance/pipeline.py +611 -0
- nc1709/performance/tiering.py +543 -0
- nc1709/plan_mode.py +362 -0
- nc1709/plugins/__init__.py +17 -0
- nc1709/plugins/agents/__init__.py +18 -0
- nc1709/plugins/agents/django_agent.py +912 -0
- nc1709/plugins/agents/docker_agent.py +623 -0
- nc1709/plugins/agents/fastapi_agent.py +887 -0
- nc1709/plugins/agents/git_agent.py +731 -0
- nc1709/plugins/agents/nextjs_agent.py +867 -0
- nc1709/plugins/base.py +359 -0
- nc1709/plugins/manager.py +411 -0
- nc1709/plugins/registry.py +337 -0
- nc1709/progress.py +443 -0
- nc1709/prompts/__init__.py +22 -0
- nc1709/prompts/agent_system.py +180 -0
- nc1709/prompts/task_prompts.py +340 -0
- nc1709/prompts/unified_prompt.py +133 -0
- nc1709/reasoning_engine.py +541 -0
- nc1709/remote_client.py +266 -0
- nc1709/shell_completions.py +349 -0
- nc1709/slash_commands.py +649 -0
- nc1709/task_classifier.py +408 -0
- nc1709/version_check.py +177 -0
- nc1709/web/__init__.py +8 -0
- nc1709/web/server.py +950 -0
- nc1709/web/templates/index.html +1127 -0
- nc1709-1.15.4.dist-info/METADATA +858 -0
- nc1709-1.15.4.dist-info/RECORD +86 -0
- nc1709-1.15.4.dist-info/WHEEL +5 -0
- nc1709-1.15.4.dist-info/entry_points.txt +2 -0
- nc1709-1.15.4.dist-info/licenses/LICENSE +9 -0
- nc1709-1.15.4.dist-info/top_level.txt +1 -0
nc1709/plan_mode.py
ADDED
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Plan Mode for NC1709
|
|
3
|
+
|
|
4
|
+
Provides planning capabilities similar to Claude Code's plan mode:
|
|
5
|
+
- Think through tasks before executing
|
|
6
|
+
- Generate step-by-step plans
|
|
7
|
+
- Review and approve plans before execution
|
|
8
|
+
- Iterate on plans with user feedback
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from typing import Optional, List, Dict, Any
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from enum import Enum
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PlanStatus(Enum):
|
|
18
|
+
"""Status of a plan"""
|
|
19
|
+
DRAFT = "draft"
|
|
20
|
+
PENDING_APPROVAL = "pending_approval"
|
|
21
|
+
APPROVED = "approved"
|
|
22
|
+
REJECTED = "rejected"
|
|
23
|
+
IN_PROGRESS = "in_progress"
|
|
24
|
+
COMPLETED = "completed"
|
|
25
|
+
FAILED = "failed"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class PlanStep:
|
|
30
|
+
"""A single step in a plan"""
|
|
31
|
+
id: int
|
|
32
|
+
description: str
|
|
33
|
+
details: Optional[str] = None
|
|
34
|
+
status: str = "pending" # pending, in_progress, completed, skipped, failed
|
|
35
|
+
result: Optional[str] = None
|
|
36
|
+
files_affected: List[str] = field(default_factory=list)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class Plan:
|
|
41
|
+
"""A complete execution plan"""
|
|
42
|
+
id: str
|
|
43
|
+
title: str
|
|
44
|
+
description: str
|
|
45
|
+
steps: List[PlanStep]
|
|
46
|
+
status: PlanStatus = PlanStatus.DRAFT
|
|
47
|
+
created_at: datetime = field(default_factory=datetime.now)
|
|
48
|
+
approved_at: Optional[datetime] = None
|
|
49
|
+
completed_at: Optional[datetime] = None
|
|
50
|
+
user_feedback: Optional[str] = None
|
|
51
|
+
estimated_files: List[str] = field(default_factory=list)
|
|
52
|
+
risks: List[str] = field(default_factory=list)
|
|
53
|
+
alternatives: List[str] = field(default_factory=list)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class PlanManager:
|
|
57
|
+
"""
|
|
58
|
+
Manages plan mode operations.
|
|
59
|
+
|
|
60
|
+
Features:
|
|
61
|
+
- Create and store plans
|
|
62
|
+
- Track plan status and progress
|
|
63
|
+
- Support plan approval workflow
|
|
64
|
+
- Persist plans for review
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def __init__(self):
|
|
68
|
+
self._current_plan: Optional[Plan] = None
|
|
69
|
+
self._plan_history: List[Plan] = []
|
|
70
|
+
self._plan_mode_active: bool = False
|
|
71
|
+
self._plan_counter: int = 0
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def is_plan_mode(self) -> bool:
|
|
75
|
+
"""Check if plan mode is active"""
|
|
76
|
+
return self._plan_mode_active
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def current_plan(self) -> Optional[Plan]:
|
|
80
|
+
"""Get the current plan"""
|
|
81
|
+
return self._current_plan
|
|
82
|
+
|
|
83
|
+
def enter_plan_mode(self) -> None:
|
|
84
|
+
"""Enter plan mode"""
|
|
85
|
+
self._plan_mode_active = True
|
|
86
|
+
|
|
87
|
+
def exit_plan_mode(self) -> None:
|
|
88
|
+
"""Exit plan mode"""
|
|
89
|
+
self._plan_mode_active = False
|
|
90
|
+
|
|
91
|
+
def create_plan(
|
|
92
|
+
self,
|
|
93
|
+
title: str,
|
|
94
|
+
description: str,
|
|
95
|
+
steps: List[Dict[str, Any]],
|
|
96
|
+
estimated_files: Optional[List[str]] = None,
|
|
97
|
+
risks: Optional[List[str]] = None,
|
|
98
|
+
alternatives: Optional[List[str]] = None
|
|
99
|
+
) -> Plan:
|
|
100
|
+
"""
|
|
101
|
+
Create a new plan.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
title: Plan title
|
|
105
|
+
description: Plan description
|
|
106
|
+
steps: List of step dictionaries with description and optional details
|
|
107
|
+
estimated_files: List of files that may be affected
|
|
108
|
+
risks: List of potential risks
|
|
109
|
+
alternatives: List of alternative approaches
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
The created Plan
|
|
113
|
+
"""
|
|
114
|
+
self._plan_counter += 1
|
|
115
|
+
plan_id = f"plan_{self._plan_counter}_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
|
116
|
+
|
|
117
|
+
plan_steps = []
|
|
118
|
+
for i, step_data in enumerate(steps, 1):
|
|
119
|
+
step = PlanStep(
|
|
120
|
+
id=i,
|
|
121
|
+
description=step_data.get("description", ""),
|
|
122
|
+
details=step_data.get("details"),
|
|
123
|
+
files_affected=step_data.get("files", [])
|
|
124
|
+
)
|
|
125
|
+
plan_steps.append(step)
|
|
126
|
+
|
|
127
|
+
plan = Plan(
|
|
128
|
+
id=plan_id,
|
|
129
|
+
title=title,
|
|
130
|
+
description=description,
|
|
131
|
+
steps=plan_steps,
|
|
132
|
+
status=PlanStatus.PENDING_APPROVAL,
|
|
133
|
+
estimated_files=estimated_files or [],
|
|
134
|
+
risks=risks or [],
|
|
135
|
+
alternatives=alternatives or []
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
self._current_plan = plan
|
|
139
|
+
return plan
|
|
140
|
+
|
|
141
|
+
def approve_plan(self) -> bool:
|
|
142
|
+
"""Approve the current plan for execution"""
|
|
143
|
+
if not self._current_plan:
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
self._current_plan.status = PlanStatus.APPROVED
|
|
147
|
+
self._current_plan.approved_at = datetime.now()
|
|
148
|
+
return True
|
|
149
|
+
|
|
150
|
+
def reject_plan(self, feedback: Optional[str] = None) -> bool:
|
|
151
|
+
"""Reject the current plan"""
|
|
152
|
+
if not self._current_plan:
|
|
153
|
+
return False
|
|
154
|
+
|
|
155
|
+
self._current_plan.status = PlanStatus.REJECTED
|
|
156
|
+
self._current_plan.user_feedback = feedback
|
|
157
|
+
self._plan_history.append(self._current_plan)
|
|
158
|
+
self._current_plan = None
|
|
159
|
+
return True
|
|
160
|
+
|
|
161
|
+
def start_execution(self) -> bool:
|
|
162
|
+
"""Start executing the approved plan"""
|
|
163
|
+
if not self._current_plan or self._current_plan.status != PlanStatus.APPROVED:
|
|
164
|
+
return False
|
|
165
|
+
|
|
166
|
+
self._current_plan.status = PlanStatus.IN_PROGRESS
|
|
167
|
+
return True
|
|
168
|
+
|
|
169
|
+
def update_step(self, step_id: int, status: str, result: Optional[str] = None) -> bool:
|
|
170
|
+
"""Update a step's status"""
|
|
171
|
+
if not self._current_plan:
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
for step in self._current_plan.steps:
|
|
175
|
+
if step.id == step_id:
|
|
176
|
+
step.status = status
|
|
177
|
+
step.result = result
|
|
178
|
+
return True
|
|
179
|
+
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
def complete_plan(self, success: bool = True) -> None:
|
|
183
|
+
"""Mark the plan as complete"""
|
|
184
|
+
if not self._current_plan:
|
|
185
|
+
return
|
|
186
|
+
|
|
187
|
+
self._current_plan.status = PlanStatus.COMPLETED if success else PlanStatus.FAILED
|
|
188
|
+
self._current_plan.completed_at = datetime.now()
|
|
189
|
+
self._plan_history.append(self._current_plan)
|
|
190
|
+
self._current_plan = None
|
|
191
|
+
self._plan_mode_active = False
|
|
192
|
+
|
|
193
|
+
def get_plan_summary(self) -> Optional[str]:
|
|
194
|
+
"""Get a formatted summary of the current plan"""
|
|
195
|
+
if not self._current_plan:
|
|
196
|
+
return None
|
|
197
|
+
|
|
198
|
+
plan = self._current_plan
|
|
199
|
+
lines = []
|
|
200
|
+
|
|
201
|
+
# Header
|
|
202
|
+
lines.append(f"\n\033[1m{plan.title}\033[0m")
|
|
203
|
+
lines.append(f"\033[90mStatus: {plan.status.value}\033[0m")
|
|
204
|
+
lines.append("")
|
|
205
|
+
lines.append(plan.description)
|
|
206
|
+
lines.append("")
|
|
207
|
+
|
|
208
|
+
# Steps
|
|
209
|
+
lines.append("\033[1mSteps:\033[0m")
|
|
210
|
+
for step in plan.steps:
|
|
211
|
+
status_icon = {
|
|
212
|
+
"pending": "○",
|
|
213
|
+
"in_progress": "◐",
|
|
214
|
+
"completed": "●",
|
|
215
|
+
"skipped": "○",
|
|
216
|
+
"failed": "✗"
|
|
217
|
+
}.get(step.status, "○")
|
|
218
|
+
|
|
219
|
+
lines.append(f" {status_icon} {step.id}. {step.description}")
|
|
220
|
+
if step.details:
|
|
221
|
+
lines.append(f" \033[90m{step.details}\033[0m")
|
|
222
|
+
if step.files_affected:
|
|
223
|
+
lines.append(f" \033[36mFiles: {', '.join(step.files_affected)}\033[0m")
|
|
224
|
+
|
|
225
|
+
lines.append("")
|
|
226
|
+
|
|
227
|
+
# Files
|
|
228
|
+
if plan.estimated_files:
|
|
229
|
+
lines.append("\033[1mFiles that may be affected:\033[0m")
|
|
230
|
+
for f in plan.estimated_files:
|
|
231
|
+
lines.append(f" • {f}")
|
|
232
|
+
lines.append("")
|
|
233
|
+
|
|
234
|
+
# Risks
|
|
235
|
+
if plan.risks:
|
|
236
|
+
lines.append("\033[1;33mPotential Risks:\033[0m")
|
|
237
|
+
for risk in plan.risks:
|
|
238
|
+
lines.append(f" ⚠ {risk}")
|
|
239
|
+
lines.append("")
|
|
240
|
+
|
|
241
|
+
# Alternatives
|
|
242
|
+
if plan.alternatives:
|
|
243
|
+
lines.append("\033[1mAlternative Approaches:\033[0m")
|
|
244
|
+
for alt in plan.alternatives:
|
|
245
|
+
lines.append(f" → {alt}")
|
|
246
|
+
lines.append("")
|
|
247
|
+
|
|
248
|
+
return "\n".join(lines)
|
|
249
|
+
|
|
250
|
+
def get_execution_prompt(self) -> Optional[str]:
|
|
251
|
+
"""Get the prompt text for executing the plan"""
|
|
252
|
+
if not self._current_plan:
|
|
253
|
+
return None
|
|
254
|
+
|
|
255
|
+
steps_text = "\n".join([
|
|
256
|
+
f"{step.id}. {step.description}"
|
|
257
|
+
+ (f"\n Details: {step.details}" if step.details else "")
|
|
258
|
+
for step in self._current_plan.steps
|
|
259
|
+
])
|
|
260
|
+
|
|
261
|
+
return f"""Execute the following plan:
|
|
262
|
+
|
|
263
|
+
**{self._current_plan.title}**
|
|
264
|
+
|
|
265
|
+
{self._current_plan.description}
|
|
266
|
+
|
|
267
|
+
Steps to follow:
|
|
268
|
+
{steps_text}
|
|
269
|
+
|
|
270
|
+
Files that may be affected: {', '.join(self._current_plan.estimated_files) if self._current_plan.estimated_files else 'TBD based on steps'}
|
|
271
|
+
|
|
272
|
+
Please execute each step in order, updating progress as you go.
|
|
273
|
+
"""
|
|
274
|
+
|
|
275
|
+
def get_plan_history(self, limit: int = 10) -> List[Dict[str, Any]]:
|
|
276
|
+
"""Get recent plan history"""
|
|
277
|
+
return [
|
|
278
|
+
{
|
|
279
|
+
"id": plan.id,
|
|
280
|
+
"title": plan.title,
|
|
281
|
+
"status": plan.status.value,
|
|
282
|
+
"created": plan.created_at.isoformat(),
|
|
283
|
+
"steps_count": len(plan.steps)
|
|
284
|
+
}
|
|
285
|
+
for plan in self._plan_history[-limit:]
|
|
286
|
+
]
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
# Global plan manager
|
|
290
|
+
_plan_manager: Optional[PlanManager] = None
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def get_plan_manager() -> PlanManager:
|
|
294
|
+
"""Get or create the global plan manager"""
|
|
295
|
+
global _plan_manager
|
|
296
|
+
if _plan_manager is None:
|
|
297
|
+
_plan_manager = PlanManager()
|
|
298
|
+
return _plan_manager
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def generate_plan_from_task(task: str) -> Dict[str, Any]:
|
|
302
|
+
"""
|
|
303
|
+
Generate a plan structure from a task description.
|
|
304
|
+
|
|
305
|
+
This is a template that would be filled in by the LLM.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
task: The task description
|
|
309
|
+
|
|
310
|
+
Returns:
|
|
311
|
+
A plan structure dict
|
|
312
|
+
"""
|
|
313
|
+
return {
|
|
314
|
+
"title": f"Plan: {task[:50]}{'...' if len(task) > 50 else ''}",
|
|
315
|
+
"description": f"Implementation plan for: {task}",
|
|
316
|
+
"steps": [],
|
|
317
|
+
"estimated_files": [],
|
|
318
|
+
"risks": [],
|
|
319
|
+
"alternatives": []
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
# Plan mode prompt template
|
|
324
|
+
PLAN_MODE_SYSTEM_PROMPT = """You are in PLAN MODE. Before making any changes, you must:
|
|
325
|
+
|
|
326
|
+
1. **Analyze** the task thoroughly
|
|
327
|
+
2. **Create a plan** with clear, numbered steps
|
|
328
|
+
3. **Identify** files that will be affected
|
|
329
|
+
4. **Consider** potential risks or issues
|
|
330
|
+
5. **Present alternatives** if applicable
|
|
331
|
+
|
|
332
|
+
Format your plan as:
|
|
333
|
+
|
|
334
|
+
## Plan: [Title]
|
|
335
|
+
|
|
336
|
+
### Description
|
|
337
|
+
[Brief description of what this plan accomplishes]
|
|
338
|
+
|
|
339
|
+
### Steps
|
|
340
|
+
1. [First step]
|
|
341
|
+
2. [Second step]
|
|
342
|
+
...
|
|
343
|
+
|
|
344
|
+
### Files Affected
|
|
345
|
+
- file1.py
|
|
346
|
+
- file2.ts
|
|
347
|
+
|
|
348
|
+
### Potential Risks
|
|
349
|
+
- [Risk 1]
|
|
350
|
+
- [Risk 2]
|
|
351
|
+
|
|
352
|
+
### Alternative Approaches
|
|
353
|
+
- [Alternative 1]
|
|
354
|
+
- [Alternative 2]
|
|
355
|
+
|
|
356
|
+
After presenting the plan, ask the user if they want to:
|
|
357
|
+
- **Approve** the plan and proceed with execution
|
|
358
|
+
- **Modify** the plan with their feedback
|
|
359
|
+
- **Reject** the plan and start over
|
|
360
|
+
|
|
361
|
+
Do NOT make any file changes until the plan is approved.
|
|
362
|
+
"""
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NC1709 Plugin System
|
|
3
|
+
Extensible plugin architecture for agents and tools
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .base import Plugin, PluginMetadata, PluginCapability, PluginStatus
|
|
7
|
+
from .registry import PluginRegistry
|
|
8
|
+
from .manager import PluginManager
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"Plugin",
|
|
12
|
+
"PluginMetadata",
|
|
13
|
+
"PluginCapability",
|
|
14
|
+
"PluginStatus",
|
|
15
|
+
"PluginRegistry",
|
|
16
|
+
"PluginManager"
|
|
17
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NC1709 Built-in Agents
|
|
3
|
+
Specialized plugins for common development tasks
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .git_agent import GitAgent
|
|
7
|
+
from .docker_agent import DockerAgent
|
|
8
|
+
from .fastapi_agent import FastAPIAgent
|
|
9
|
+
from .nextjs_agent import NextJSAgent
|
|
10
|
+
from .django_agent import DjangoAgent
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"GitAgent",
|
|
14
|
+
"DockerAgent",
|
|
15
|
+
"FastAPIAgent",
|
|
16
|
+
"NextJSAgent",
|
|
17
|
+
"DjangoAgent"
|
|
18
|
+
]
|