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/remote_client.py
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""
|
|
2
|
+
NC1709 Remote Client
|
|
3
|
+
Connects to a remote NC1709 server for LLM access
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
import json
|
|
7
|
+
from typing import Optional, Dict, Any
|
|
8
|
+
from urllib.request import Request, urlopen
|
|
9
|
+
from urllib.error import URLError, HTTPError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RemoteClient:
|
|
13
|
+
"""Client for connecting to remote NC1709 server"""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
server_url: Optional[str] = None,
|
|
18
|
+
api_key: Optional[str] = None
|
|
19
|
+
):
|
|
20
|
+
"""Initialize remote client
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
server_url: URL of the NC1709 server (or set NC1709_API_URL env var)
|
|
24
|
+
api_key: API key for authentication (or set NC1709_API_KEY env var)
|
|
25
|
+
"""
|
|
26
|
+
self.server_url = (
|
|
27
|
+
server_url or
|
|
28
|
+
os.environ.get("NC1709_API_URL") or
|
|
29
|
+
os.environ.get("NC1709_SERVER_URL")
|
|
30
|
+
)
|
|
31
|
+
self.api_key = (
|
|
32
|
+
api_key or
|
|
33
|
+
os.environ.get("NC1709_API_KEY")
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
if not self.server_url:
|
|
37
|
+
raise ValueError(
|
|
38
|
+
"No server URL provided. Set NC1709_API_URL environment variable "
|
|
39
|
+
"or pass server_url parameter."
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Normalize URL
|
|
43
|
+
self.server_url = self.server_url.rstrip("/")
|
|
44
|
+
|
|
45
|
+
def _make_request(
|
|
46
|
+
self,
|
|
47
|
+
endpoint: str,
|
|
48
|
+
method: str = "GET",
|
|
49
|
+
data: Optional[Dict] = None
|
|
50
|
+
) -> Dict[str, Any]:
|
|
51
|
+
"""Make HTTP request to server
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
endpoint: API endpoint (e.g., "/api/remote/status")
|
|
55
|
+
method: HTTP method
|
|
56
|
+
data: JSON data for POST requests
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Response JSON
|
|
60
|
+
"""
|
|
61
|
+
url = f"{self.server_url}{endpoint}"
|
|
62
|
+
|
|
63
|
+
headers = {
|
|
64
|
+
"Content-Type": "application/json",
|
|
65
|
+
"User-Agent": "nc1709-client/1.4.0"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if self.api_key:
|
|
69
|
+
headers["X-API-Key"] = self.api_key
|
|
70
|
+
|
|
71
|
+
body = json.dumps(data).encode("utf-8") if data else None
|
|
72
|
+
|
|
73
|
+
req = Request(url, data=body, headers=headers, method=method)
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
with urlopen(req, timeout=120) as response:
|
|
77
|
+
return json.loads(response.read().decode("utf-8"))
|
|
78
|
+
except HTTPError as e:
|
|
79
|
+
error_body = e.read().decode("utf-8")
|
|
80
|
+
try:
|
|
81
|
+
error_json = json.loads(error_body)
|
|
82
|
+
detail = error_json.get("detail", error_body)
|
|
83
|
+
except json.JSONDecodeError:
|
|
84
|
+
detail = error_body
|
|
85
|
+
|
|
86
|
+
if e.code == 401:
|
|
87
|
+
raise ConnectionError(
|
|
88
|
+
f"Authentication failed: {detail}\n"
|
|
89
|
+
"Set NC1709_API_KEY environment variable with your API key."
|
|
90
|
+
)
|
|
91
|
+
raise ConnectionError(f"Server error ({e.code}): {detail}")
|
|
92
|
+
except URLError as e:
|
|
93
|
+
raise ConnectionError(
|
|
94
|
+
f"Cannot connect to NC1709 server at {self.server_url}\n"
|
|
95
|
+
f"Error: {e.reason}\n"
|
|
96
|
+
"Make sure the server is running and accessible."
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def check_status(self) -> Dict[str, Any]:
|
|
100
|
+
"""Check server status
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Server status information
|
|
104
|
+
"""
|
|
105
|
+
return self._make_request("/api/remote/status")
|
|
106
|
+
|
|
107
|
+
def complete(
|
|
108
|
+
self,
|
|
109
|
+
prompt: str,
|
|
110
|
+
task_type: str = "general",
|
|
111
|
+
system_prompt: Optional[str] = None,
|
|
112
|
+
temperature: float = 0.7,
|
|
113
|
+
max_tokens: Optional[int] = None
|
|
114
|
+
) -> str:
|
|
115
|
+
"""Get LLM completion from remote server
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
prompt: User prompt
|
|
119
|
+
task_type: Task type (reasoning, coding, tools, general, fast)
|
|
120
|
+
system_prompt: Optional system prompt
|
|
121
|
+
temperature: Sampling temperature
|
|
122
|
+
max_tokens: Maximum tokens
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
LLM response text
|
|
126
|
+
"""
|
|
127
|
+
data = {
|
|
128
|
+
"prompt": prompt,
|
|
129
|
+
"task_type": task_type,
|
|
130
|
+
"temperature": temperature
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if system_prompt:
|
|
134
|
+
data["system_prompt"] = system_prompt
|
|
135
|
+
if max_tokens:
|
|
136
|
+
data["max_tokens"] = max_tokens
|
|
137
|
+
|
|
138
|
+
result = self._make_request("/api/remote/complete", method="POST", data=data)
|
|
139
|
+
return result.get("response", "")
|
|
140
|
+
|
|
141
|
+
def chat(self, message: str) -> str:
|
|
142
|
+
"""Send chat message to remote server (uses full reasoning engine)
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
message: User message
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Assistant response
|
|
149
|
+
"""
|
|
150
|
+
data = {"message": message}
|
|
151
|
+
result = self._make_request("/api/remote/chat", method="POST", data=data)
|
|
152
|
+
return result.get("response", "")
|
|
153
|
+
|
|
154
|
+
def agent_chat(
|
|
155
|
+
self,
|
|
156
|
+
messages: list,
|
|
157
|
+
cwd: str,
|
|
158
|
+
tools: list = None
|
|
159
|
+
) -> Dict[str, Any]:
|
|
160
|
+
"""Send agent chat request - returns LLM response for local tool execution
|
|
161
|
+
|
|
162
|
+
This is the new architecture where:
|
|
163
|
+
- Server only runs LLM (thinking)
|
|
164
|
+
- Client executes tools locally
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
messages: Conversation history [{"role": "user/assistant", "content": "..."}]
|
|
168
|
+
cwd: Client's current working directory
|
|
169
|
+
tools: List of available tools on client
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
Dict with 'response' (LLM output that may contain tool calls)
|
|
173
|
+
"""
|
|
174
|
+
data = {
|
|
175
|
+
"messages": messages,
|
|
176
|
+
"cwd": cwd,
|
|
177
|
+
"tools": tools or []
|
|
178
|
+
}
|
|
179
|
+
return self._make_request("/api/remote/agent", method="POST", data=data)
|
|
180
|
+
|
|
181
|
+
def is_connected(self) -> bool:
|
|
182
|
+
"""Check if connected to server
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
True if server is reachable
|
|
186
|
+
"""
|
|
187
|
+
try:
|
|
188
|
+
status = self.check_status()
|
|
189
|
+
return status.get("status") == "ok"
|
|
190
|
+
except Exception:
|
|
191
|
+
return False
|
|
192
|
+
|
|
193
|
+
def index_code(
|
|
194
|
+
self,
|
|
195
|
+
user_id: str,
|
|
196
|
+
files: list,
|
|
197
|
+
project_name: str = None
|
|
198
|
+
) -> Dict[str, Any]:
|
|
199
|
+
"""Index code files on the server's vector database
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
user_id: Unique user/session identifier
|
|
203
|
+
files: List of {"path": "...", "content": "...", "language": "..."}
|
|
204
|
+
project_name: Optional project name for grouping
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
Indexing result with stats
|
|
208
|
+
"""
|
|
209
|
+
data = {
|
|
210
|
+
"user_id": user_id,
|
|
211
|
+
"files": files,
|
|
212
|
+
"project_name": project_name
|
|
213
|
+
}
|
|
214
|
+
return self._make_request("/api/remote/index", method="POST", data=data)
|
|
215
|
+
|
|
216
|
+
def search_code(
|
|
217
|
+
self,
|
|
218
|
+
user_id: str,
|
|
219
|
+
query: str,
|
|
220
|
+
n_results: int = 5,
|
|
221
|
+
project_name: str = None
|
|
222
|
+
) -> Dict[str, Any]:
|
|
223
|
+
"""Search indexed code on the server
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
user_id: User identifier
|
|
227
|
+
query: Search query
|
|
228
|
+
n_results: Number of results to return
|
|
229
|
+
project_name: Optional project filter
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
Search results
|
|
233
|
+
"""
|
|
234
|
+
data = {
|
|
235
|
+
"user_id": user_id,
|
|
236
|
+
"query": query,
|
|
237
|
+
"n_results": n_results,
|
|
238
|
+
"project_name": project_name
|
|
239
|
+
}
|
|
240
|
+
return self._make_request("/api/remote/search", method="POST", data=data)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def get_remote_client() -> Optional[RemoteClient]:
|
|
244
|
+
"""Get remote client if configured
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
RemoteClient instance if NC1709_API_URL is set, None otherwise
|
|
248
|
+
"""
|
|
249
|
+
if os.environ.get("NC1709_API_URL") or os.environ.get("NC1709_SERVER_URL"):
|
|
250
|
+
try:
|
|
251
|
+
return RemoteClient()
|
|
252
|
+
except ValueError:
|
|
253
|
+
return None
|
|
254
|
+
return None
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def is_remote_mode() -> bool:
|
|
258
|
+
"""Check if running in remote mode
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
True if NC1709_API_URL is set
|
|
262
|
+
"""
|
|
263
|
+
return bool(
|
|
264
|
+
os.environ.get("NC1709_API_URL") or
|
|
265
|
+
os.environ.get("NC1709_SERVER_URL")
|
|
266
|
+
)
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shell Completions Generator
|
|
3
|
+
Generates completion scripts for bash, zsh, and fish shells
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Bash completion script
|
|
10
|
+
BASH_COMPLETION = '''
|
|
11
|
+
# NC1709 Bash Completion
|
|
12
|
+
# Add to ~/.bashrc or ~/.bash_profile:
|
|
13
|
+
# source <(nc1709 --completion bash)
|
|
14
|
+
# or: eval "$(nc1709 --completion bash)"
|
|
15
|
+
|
|
16
|
+
_nc1709_completions() {
|
|
17
|
+
local cur prev opts
|
|
18
|
+
COMPREPLY=()
|
|
19
|
+
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
20
|
+
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
21
|
+
|
|
22
|
+
# Main options
|
|
23
|
+
opts="--help --version --web --shell --config --resume --remote --api-key --serve --port --plugins --plugin --completion --mcp-serve"
|
|
24
|
+
|
|
25
|
+
# Shell commands (when in interactive mode)
|
|
26
|
+
shell_commands="help exit quit clear history config sessions index search plugins git docker mcp"
|
|
27
|
+
|
|
28
|
+
# Plugin commands
|
|
29
|
+
plugin_opts="git:status git:diff git:log git:branch git:commit git:push git:pull docker:ps docker:images docker:logs docker:compose_up docker:compose_down fastapi:scaffold nextjs:scaffold django:scaffold"
|
|
30
|
+
|
|
31
|
+
case "${prev}" in
|
|
32
|
+
--plugin)
|
|
33
|
+
COMPREPLY=( $(compgen -W "${plugin_opts}" -- ${cur}) )
|
|
34
|
+
return 0
|
|
35
|
+
;;
|
|
36
|
+
--completion)
|
|
37
|
+
COMPREPLY=( $(compgen -W "bash zsh fish" -- ${cur}) )
|
|
38
|
+
return 0
|
|
39
|
+
;;
|
|
40
|
+
--port)
|
|
41
|
+
COMPREPLY=()
|
|
42
|
+
return 0
|
|
43
|
+
;;
|
|
44
|
+
--remote|--api-key)
|
|
45
|
+
COMPREPLY=()
|
|
46
|
+
return 0
|
|
47
|
+
;;
|
|
48
|
+
--resume)
|
|
49
|
+
# Could add session completion here
|
|
50
|
+
COMPREPLY=()
|
|
51
|
+
return 0
|
|
52
|
+
;;
|
|
53
|
+
esac
|
|
54
|
+
|
|
55
|
+
# Complete options or files
|
|
56
|
+
if [[ ${cur} == -* ]]; then
|
|
57
|
+
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
|
58
|
+
else
|
|
59
|
+
# Complete files for potential file paths
|
|
60
|
+
COMPREPLY=( $(compgen -f -- ${cur}) )
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
return 0
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
complete -F _nc1709_completions nc1709
|
|
67
|
+
'''
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# Zsh completion script
|
|
71
|
+
ZSH_COMPLETION = '''
|
|
72
|
+
#compdef nc1709
|
|
73
|
+
|
|
74
|
+
# NC1709 Zsh Completion
|
|
75
|
+
# Add to ~/.zshrc:
|
|
76
|
+
# source <(nc1709 --completion zsh)
|
|
77
|
+
# or: eval "$(nc1709 --completion zsh)"
|
|
78
|
+
# or save to ~/.zsh/completions/_nc1709
|
|
79
|
+
|
|
80
|
+
_nc1709() {
|
|
81
|
+
local -a commands
|
|
82
|
+
local -a options
|
|
83
|
+
local -a plugins
|
|
84
|
+
|
|
85
|
+
options=(
|
|
86
|
+
'--help[Show help message]'
|
|
87
|
+
'--version[Show version]'
|
|
88
|
+
'--web[Start web dashboard]'
|
|
89
|
+
'--shell[Start interactive shell]'
|
|
90
|
+
'--config[Show configuration]'
|
|
91
|
+
'--resume[Resume session]:session_id:'
|
|
92
|
+
'--remote[Remote server URL]:url:'
|
|
93
|
+
'--api-key[API key for remote server]:key:'
|
|
94
|
+
'--serve[Enable remote access]'
|
|
95
|
+
'--port[Server port]:port:'
|
|
96
|
+
'--plugins[List available plugins]'
|
|
97
|
+
'--plugin[Run a plugin]:plugin:->plugins'
|
|
98
|
+
'--completion[Generate shell completion]:shell:(bash zsh fish)'
|
|
99
|
+
'--mcp-serve[Start MCP server]'
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
plugins=(
|
|
103
|
+
'git\\:status:Show git status'
|
|
104
|
+
'git\\:diff:Show git diff'
|
|
105
|
+
'git\\:log:Show git log'
|
|
106
|
+
'git\\:branch:List branches'
|
|
107
|
+
'git\\:commit:Create commit'
|
|
108
|
+
'git\\:push:Push to remote'
|
|
109
|
+
'git\\:pull:Pull from remote'
|
|
110
|
+
'docker\\:ps:List containers'
|
|
111
|
+
'docker\\:images:List images'
|
|
112
|
+
'docker\\:logs:View logs'
|
|
113
|
+
'docker\\:compose_up:Start compose'
|
|
114
|
+
'docker\\:compose_down:Stop compose'
|
|
115
|
+
'fastapi\\:scaffold:Create FastAPI project'
|
|
116
|
+
'nextjs\\:scaffold:Create Next.js project'
|
|
117
|
+
'django\\:scaffold:Create Django project'
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
case $state in
|
|
121
|
+
plugins)
|
|
122
|
+
_describe 'plugin' plugins
|
|
123
|
+
;;
|
|
124
|
+
*)
|
|
125
|
+
_arguments -s $options
|
|
126
|
+
;;
|
|
127
|
+
esac
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
_nc1709 "$@"
|
|
131
|
+
'''
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# Fish completion script
|
|
135
|
+
FISH_COMPLETION = '''
|
|
136
|
+
# NC1709 Fish Completion
|
|
137
|
+
# Add to ~/.config/fish/completions/nc1709.fish
|
|
138
|
+
# or: nc1709 --completion fish > ~/.config/fish/completions/nc1709.fish
|
|
139
|
+
|
|
140
|
+
# Main options
|
|
141
|
+
complete -c nc1709 -l help -d "Show help message"
|
|
142
|
+
complete -c nc1709 -l version -d "Show version"
|
|
143
|
+
complete -c nc1709 -l web -d "Start web dashboard"
|
|
144
|
+
complete -c nc1709 -l shell -d "Start interactive shell"
|
|
145
|
+
complete -c nc1709 -l config -d "Show configuration"
|
|
146
|
+
complete -c nc1709 -l resume -d "Resume session" -x
|
|
147
|
+
complete -c nc1709 -l remote -d "Remote server URL" -x
|
|
148
|
+
complete -c nc1709 -l api-key -d "API key for remote server" -x
|
|
149
|
+
complete -c nc1709 -l serve -d "Enable remote access"
|
|
150
|
+
complete -c nc1709 -l port -d "Server port" -x
|
|
151
|
+
complete -c nc1709 -l plugins -d "List available plugins"
|
|
152
|
+
complete -c nc1709 -l plugin -d "Run a plugin" -xa "
|
|
153
|
+
git:status\t'Show git status'
|
|
154
|
+
git:diff\t'Show git diff'
|
|
155
|
+
git:log\t'Show git log'
|
|
156
|
+
git:branch\t'List branches'
|
|
157
|
+
git:commit\t'Create commit'
|
|
158
|
+
git:push\t'Push to remote'
|
|
159
|
+
git:pull\t'Pull from remote'
|
|
160
|
+
docker:ps\t'List containers'
|
|
161
|
+
docker:images\t'List images'
|
|
162
|
+
docker:logs\t'View logs'
|
|
163
|
+
docker:compose_up\t'Start compose'
|
|
164
|
+
docker:compose_down\t'Stop compose'
|
|
165
|
+
fastapi:scaffold\t'Create FastAPI project'
|
|
166
|
+
nextjs:scaffold\t'Create Next.js project'
|
|
167
|
+
django:scaffold\t'Create Django project'
|
|
168
|
+
"
|
|
169
|
+
complete -c nc1709 -l completion -d "Generate shell completion" -xa "bash zsh fish"
|
|
170
|
+
complete -c nc1709 -l mcp-serve -d "Start MCP server"
|
|
171
|
+
|
|
172
|
+
# File completion for positional arguments
|
|
173
|
+
complete -c nc1709 -f -a "(__fish_complete_path)"
|
|
174
|
+
'''
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def get_completion_script(shell: str) -> str:
|
|
178
|
+
"""Get completion script for specified shell
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
shell: Shell type (bash, zsh, fish)
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
Completion script content
|
|
185
|
+
"""
|
|
186
|
+
scripts = {
|
|
187
|
+
"bash": BASH_COMPLETION,
|
|
188
|
+
"zsh": ZSH_COMPLETION,
|
|
189
|
+
"fish": FISH_COMPLETION
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
script = scripts.get(shell.lower())
|
|
193
|
+
if not script:
|
|
194
|
+
raise ValueError(f"Unknown shell: {shell}. Supported: bash, zsh, fish")
|
|
195
|
+
|
|
196
|
+
return script.strip()
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def install_completions(shell: Optional[str] = None) -> str:
|
|
200
|
+
"""Install completions for the detected or specified shell
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
shell: Shell type (auto-detect if None)
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
Installation status message
|
|
207
|
+
"""
|
|
208
|
+
if shell is None:
|
|
209
|
+
shell = detect_shell()
|
|
210
|
+
|
|
211
|
+
script = get_completion_script(shell)
|
|
212
|
+
home = os.path.expanduser("~")
|
|
213
|
+
|
|
214
|
+
if shell == "bash":
|
|
215
|
+
# Try to install to .bash_completion.d or .bashrc
|
|
216
|
+
completion_dir = os.path.join(home, ".bash_completion.d")
|
|
217
|
+
if os.path.isdir(completion_dir):
|
|
218
|
+
path = os.path.join(completion_dir, "nc1709")
|
|
219
|
+
with open(path, "w") as f:
|
|
220
|
+
f.write(script)
|
|
221
|
+
return f"Installed to {path}. Restart your shell to use completions."
|
|
222
|
+
else:
|
|
223
|
+
# Add to .bashrc
|
|
224
|
+
bashrc = os.path.join(home, ".bashrc")
|
|
225
|
+
source_line = '\n# NC1709 completions\neval "$(nc1709 --completion bash)"\n'
|
|
226
|
+
with open(bashrc, "a") as f:
|
|
227
|
+
f.write(source_line)
|
|
228
|
+
return f"Added to {bashrc}. Run 'source ~/.bashrc' or restart your shell."
|
|
229
|
+
|
|
230
|
+
elif shell == "zsh":
|
|
231
|
+
# Install to .zsh/completions or add to .zshrc
|
|
232
|
+
completion_dir = os.path.join(home, ".zsh", "completions")
|
|
233
|
+
os.makedirs(completion_dir, exist_ok=True)
|
|
234
|
+
path = os.path.join(completion_dir, "_nc1709")
|
|
235
|
+
with open(path, "w") as f:
|
|
236
|
+
f.write(script)
|
|
237
|
+
|
|
238
|
+
# Ensure completions dir is in fpath
|
|
239
|
+
zshrc = os.path.join(home, ".zshrc")
|
|
240
|
+
fpath_line = f'\nfpath=({completion_dir} $fpath)\nautoload -Uz compinit && compinit\n'
|
|
241
|
+
|
|
242
|
+
# Check if already added
|
|
243
|
+
try:
|
|
244
|
+
with open(zshrc, "r") as f:
|
|
245
|
+
content = f.read()
|
|
246
|
+
if completion_dir not in content:
|
|
247
|
+
with open(zshrc, "a") as f:
|
|
248
|
+
f.write(fpath_line)
|
|
249
|
+
except FileNotFoundError:
|
|
250
|
+
with open(zshrc, "w") as f:
|
|
251
|
+
f.write(fpath_line)
|
|
252
|
+
|
|
253
|
+
return f"Installed to {path}. Run 'source ~/.zshrc' or restart your shell."
|
|
254
|
+
|
|
255
|
+
elif shell == "fish":
|
|
256
|
+
# Install to fish completions directory
|
|
257
|
+
completion_dir = os.path.join(home, ".config", "fish", "completions")
|
|
258
|
+
os.makedirs(completion_dir, exist_ok=True)
|
|
259
|
+
path = os.path.join(completion_dir, "nc1709.fish")
|
|
260
|
+
with open(path, "w") as f:
|
|
261
|
+
f.write(script)
|
|
262
|
+
return f"Installed to {path}. Completions will be available in new fish sessions."
|
|
263
|
+
|
|
264
|
+
else:
|
|
265
|
+
raise ValueError(f"Cannot install completions for shell: {shell}")
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def detect_shell() -> str:
|
|
269
|
+
"""Detect the current shell
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
Shell name (bash, zsh, or fish)
|
|
273
|
+
"""
|
|
274
|
+
shell_path = os.environ.get("SHELL", "")
|
|
275
|
+
shell_name = os.path.basename(shell_path)
|
|
276
|
+
|
|
277
|
+
if "zsh" in shell_name:
|
|
278
|
+
return "zsh"
|
|
279
|
+
elif "fish" in shell_name:
|
|
280
|
+
return "fish"
|
|
281
|
+
else:
|
|
282
|
+
return "bash"
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def print_installation_instructions(shell: str) -> None:
|
|
286
|
+
"""Print installation instructions for a shell
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
shell: Shell type
|
|
290
|
+
"""
|
|
291
|
+
instructions = {
|
|
292
|
+
"bash": """
|
|
293
|
+
# Bash Completion Installation
|
|
294
|
+
|
|
295
|
+
Option 1: Add to .bashrc (recommended)
|
|
296
|
+
echo 'eval "$(nc1709 --completion bash)"' >> ~/.bashrc
|
|
297
|
+
source ~/.bashrc
|
|
298
|
+
|
|
299
|
+
Option 2: Create completion file
|
|
300
|
+
nc1709 --completion bash > ~/.bash_completion.d/nc1709
|
|
301
|
+
# Requires bash-completion to be installed
|
|
302
|
+
""",
|
|
303
|
+
"zsh": """
|
|
304
|
+
# Zsh Completion Installation
|
|
305
|
+
|
|
306
|
+
Option 1: Add to .zshrc (simplest)
|
|
307
|
+
echo 'eval "$(nc1709 --completion zsh)"' >> ~/.zshrc
|
|
308
|
+
source ~/.zshrc
|
|
309
|
+
|
|
310
|
+
Option 2: Save to completions directory
|
|
311
|
+
mkdir -p ~/.zsh/completions
|
|
312
|
+
nc1709 --completion zsh > ~/.zsh/completions/_nc1709
|
|
313
|
+
# Add to .zshrc: fpath=(~/.zsh/completions $fpath)
|
|
314
|
+
# Then run: autoload -Uz compinit && compinit
|
|
315
|
+
""",
|
|
316
|
+
"fish": """
|
|
317
|
+
# Fish Completion Installation
|
|
318
|
+
|
|
319
|
+
nc1709 --completion fish > ~/.config/fish/completions/nc1709.fish
|
|
320
|
+
|
|
321
|
+
# Completions will be available immediately in new shells
|
|
322
|
+
"""
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
print(instructions.get(shell, f"Unknown shell: {shell}"))
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
# Entry point for CLI
|
|
329
|
+
def handle_completion_command(args: list) -> None:
|
|
330
|
+
"""Handle completion command from CLI
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
args: Command arguments (e.g., ['bash'] or ['--install', 'zsh'])
|
|
334
|
+
"""
|
|
335
|
+
if not args:
|
|
336
|
+
# Print detected shell and instructions
|
|
337
|
+
shell = detect_shell()
|
|
338
|
+
print(f"Detected shell: {shell}")
|
|
339
|
+
print_installation_instructions(shell)
|
|
340
|
+
return
|
|
341
|
+
|
|
342
|
+
if args[0] == "--install":
|
|
343
|
+
shell = args[1] if len(args) > 1 else None
|
|
344
|
+
result = install_completions(shell)
|
|
345
|
+
print(result)
|
|
346
|
+
else:
|
|
347
|
+
# Print completion script
|
|
348
|
+
shell = args[0]
|
|
349
|
+
print(get_completion_script(shell))
|