gitinstall 1.1.0__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 (59) hide show
  1. gitinstall/__init__.py +61 -0
  2. gitinstall/_sdk.py +541 -0
  3. gitinstall/academic.py +831 -0
  4. gitinstall/admin.html +327 -0
  5. gitinstall/auto_update.py +384 -0
  6. gitinstall/autopilot.py +349 -0
  7. gitinstall/badge.py +476 -0
  8. gitinstall/checkpoint.py +330 -0
  9. gitinstall/cicd.py +499 -0
  10. gitinstall/clawhub.html +718 -0
  11. gitinstall/config_schema.py +353 -0
  12. gitinstall/db.py +984 -0
  13. gitinstall/db_backend.py +445 -0
  14. gitinstall/dep_chain.py +337 -0
  15. gitinstall/dependency_audit.py +1153 -0
  16. gitinstall/detector.py +542 -0
  17. gitinstall/doctor.py +493 -0
  18. gitinstall/education.py +869 -0
  19. gitinstall/enterprise.py +802 -0
  20. gitinstall/error_fixer.py +953 -0
  21. gitinstall/event_bus.py +251 -0
  22. gitinstall/executor.py +577 -0
  23. gitinstall/feature_flags.py +138 -0
  24. gitinstall/fetcher.py +921 -0
  25. gitinstall/huggingface.py +922 -0
  26. gitinstall/hw_detect.py +988 -0
  27. gitinstall/i18n.py +664 -0
  28. gitinstall/installer_registry.py +362 -0
  29. gitinstall/knowledge_base.py +379 -0
  30. gitinstall/license_check.py +605 -0
  31. gitinstall/llm.py +569 -0
  32. gitinstall/log.py +236 -0
  33. gitinstall/main.py +1408 -0
  34. gitinstall/mcp_agent.py +841 -0
  35. gitinstall/mcp_server.py +386 -0
  36. gitinstall/monorepo.py +810 -0
  37. gitinstall/multi_source.py +425 -0
  38. gitinstall/onboard.py +276 -0
  39. gitinstall/planner.py +222 -0
  40. gitinstall/planner_helpers.py +323 -0
  41. gitinstall/planner_known_projects.py +1010 -0
  42. gitinstall/planner_templates.py +996 -0
  43. gitinstall/remote_gpu.py +633 -0
  44. gitinstall/resilience.py +608 -0
  45. gitinstall/run_tests.py +572 -0
  46. gitinstall/skills.py +476 -0
  47. gitinstall/tool_schemas.py +324 -0
  48. gitinstall/trending.py +279 -0
  49. gitinstall/uninstaller.py +415 -0
  50. gitinstall/validate_top100.py +607 -0
  51. gitinstall/watchdog.py +180 -0
  52. gitinstall/web.py +1277 -0
  53. gitinstall/web_ui.html +2277 -0
  54. gitinstall-1.1.0.dist-info/METADATA +275 -0
  55. gitinstall-1.1.0.dist-info/RECORD +59 -0
  56. gitinstall-1.1.0.dist-info/WHEEL +5 -0
  57. gitinstall-1.1.0.dist-info/entry_points.txt +3 -0
  58. gitinstall-1.1.0.dist-info/licenses/LICENSE +21 -0
  59. gitinstall-1.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,386 @@
1
+ #!/usr/bin/env python3
2
+ """gitinstall MCP Server — AI Agent 通过 MCP 协议调用安装引擎
3
+ ============================================================
4
+
5
+ 零外部依赖。通过 stdio JSON-RPC 2.0 与客户端通信。
6
+
7
+ 启动::
8
+
9
+ gitinstall mcp # CLI 子命令
10
+ gitinstall-mcp # 独立入口(pip install 后可用)
11
+ python -m gitinstall.mcp_server # 模块运行
12
+
13
+ Claude Desktop 配置 (~/.claude/claude_desktop_config.json)::
14
+
15
+ {
16
+ "mcpServers": {
17
+ "gitinstall": {
18
+ "command": "gitinstall-mcp"
19
+ }
20
+ }
21
+ }
22
+
23
+ 协议: MCP (Model Context Protocol) 2024-11-05
24
+ 传输: stdio, newline-delimited JSON
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ import json
30
+ import logging
31
+ import sys
32
+ from pathlib import Path
33
+
34
+ # ── 确保 bare import 可用 ────────────────────────────────
35
+ _THIS_DIR = Path(__file__).parent
36
+ if str(_THIS_DIR) not in sys.path:
37
+ sys.path.insert(0, str(_THIS_DIR))
38
+
39
+ # ── MCP 用 stdout 通信 — 日志全部走 stderr ────────────────
40
+ logging.basicConfig(
41
+ stream=sys.stderr,
42
+ level=logging.INFO,
43
+ format="%(asctime)s [MCP] %(message)s",
44
+ datefmt="%H:%M:%S",
45
+ )
46
+ logger = logging.getLogger("gitinstall.mcp")
47
+
48
+ from _sdk import (
49
+ detect as _detect,
50
+ install as _install,
51
+ diagnose as _diagnose,
52
+ fetch as _fetch,
53
+ doctor as _doctor,
54
+ audit as _audit,
55
+ __version__,
56
+ )
57
+
58
+ PROTOCOL_VERSION = "2024-11-05"
59
+
60
+ # ── Tool Definitions ─────────────────────────────────────
61
+
62
+ TOOLS = [
63
+ {
64
+ "name": "install_github_project",
65
+ "description": (
66
+ "Install any GitHub open-source project on the user's machine. "
67
+ "Handles the ENTIRE process: detects OS/GPU/runtimes → fetches project info → "
68
+ "generates installation plan (30+ languages: Python, Node, Rust, Go, C++, Haskell, Zig...) → "
69
+ "executes safely (dangerous commands blocked) → auto-fixes errors (28 patterns: "
70
+ "missing tools, permission denied, PEP 668, CUDA mismatch, etc.) → retries with "
71
+ "fallback strategies if needed. Works WITHOUT any LLM by default (pure rule engine). "
72
+ "Supports: GitHub URLs, 'owner/repo' format, GitLab/Gitee/Bitbucket URLs. "
73
+ "Example: install_github_project({\"project\": \"comfyanonymous/ComfyUI\"}) "
74
+ "Call this when a user asks you to install, set up, or run any GitHub project."
75
+ ),
76
+ "inputSchema": {
77
+ "type": "object",
78
+ "properties": {
79
+ "project": {
80
+ "type": "string",
81
+ "description": "GitHub project: 'owner/repo' or full URL (e.g. 'pytorch/pytorch' or 'https://github.com/user/repo')",
82
+ },
83
+ "install_dir": {
84
+ "type": "string",
85
+ "description": "Target directory. Default: ~/github-installs/<project>/",
86
+ },
87
+ "dry_run": {
88
+ "type": "boolean",
89
+ "description": "If true, show the plan without executing. Default: false.",
90
+ },
91
+ },
92
+ "required": ["project"],
93
+ },
94
+ },
95
+ {
96
+ "name": "diagnose_install_error",
97
+ "description": (
98
+ "Diagnose a software installation or build error and return actionable fix commands. "
99
+ "Covers 28 error patterns including: missing CLI tools (with cross-platform install mapping "
100
+ "for 40+ tools), pip permission/PEP 668/package name typos, npm EACCES/workspace protocol, "
101
+ "Python/Node version mismatches, Rust/Go/CMake missing build deps, Haskell toolchain issues "
102
+ "(ghcup/cabal/stack), Zig SDK compatibility, port conflicts, disk space, git submodule failures. "
103
+ "Returns root cause analysis and ready-to-run fix commands. "
104
+ "Call this when a user shows you an error from pip/npm/cargo/go/cmake/make or any build tool."
105
+ ),
106
+ "inputSchema": {
107
+ "type": "object",
108
+ "properties": {
109
+ "stderr": {
110
+ "type": "string",
111
+ "description": "The error output text to diagnose",
112
+ },
113
+ "command": {
114
+ "type": "string",
115
+ "description": "The command that produced the error (e.g. 'pip install torch')",
116
+ },
117
+ "stdout": {
118
+ "type": "string",
119
+ "description": "Standard output text, if available",
120
+ },
121
+ },
122
+ "required": ["stderr"],
123
+ },
124
+ },
125
+ {
126
+ "name": "detect_environment",
127
+ "description": (
128
+ "Detect the user's development environment. Returns: OS (macOS/Linux/Windows/WSL2), "
129
+ "CPU (x86_64/arm64, Apple Silicon M1-M4), GPU (CUDA version/ROCm/Apple MPS/CPU-only), "
130
+ "RAM and disk space, installed runtimes with versions (Python, Node, Go, Rust, Java, Docker, "
131
+ "Git, FFmpeg...), available package managers (pip, conda, brew, apt, npm, cargo...). "
132
+ "Call this to understand what the user's system can do before suggesting installations."
133
+ ),
134
+ "inputSchema": {
135
+ "type": "object",
136
+ "properties": {},
137
+ },
138
+ },
139
+ {
140
+ "name": "get_project_info",
141
+ "description": (
142
+ "Fetch metadata about a GitHub project WITHOUT installing it. "
143
+ "Returns: project name, description, star count, primary language, license, "
144
+ "detected project type (e.g. 'python-ml', 'node-webapp', 'rust-cli'), "
145
+ "clone URL, homepage, dependency files list, and README preview. "
146
+ "Call this to learn about a project before deciding whether to install it."
147
+ ),
148
+ "inputSchema": {
149
+ "type": "object",
150
+ "properties": {
151
+ "project": {
152
+ "type": "string",
153
+ "description": "GitHub project: 'owner/repo' or full URL",
154
+ },
155
+ },
156
+ "required": ["project"],
157
+ },
158
+ },
159
+ {
160
+ "name": "check_system_health",
161
+ "description": (
162
+ "Run a comprehensive system diagnostic. Checks all development tools, "
163
+ "runtime versions, common configuration issues, PATH problems, and system health. "
164
+ "Returns a checklist with OK/Warning/Error status and specific fix commands. "
165
+ "Call this when installation keeps failing or the user reports environment issues."
166
+ ),
167
+ "inputSchema": {
168
+ "type": "object",
169
+ "properties": {},
170
+ },
171
+ },
172
+ {
173
+ "name": "audit_dependencies",
174
+ "description": (
175
+ "Audit a GitHub project's dependencies for known security vulnerabilities (CVEs). "
176
+ "Scans requirements.txt, package.json, Cargo.toml, go.mod etc. "
177
+ "Call this before installing untrusted projects."
178
+ ),
179
+ "inputSchema": {
180
+ "type": "object",
181
+ "properties": {
182
+ "project": {
183
+ "type": "string",
184
+ "description": "GitHub project: 'owner/repo' or full URL",
185
+ },
186
+ "online": {
187
+ "type": "boolean",
188
+ "description": "Query online CVE databases for thorough results. Default: false.",
189
+ },
190
+ },
191
+ "required": ["project"],
192
+ },
193
+ },
194
+ ]
195
+
196
+
197
+ # ── I/O ──────────────────────────────────────────────────
198
+
199
+ def _send(msg: dict) -> None:
200
+ """Write one JSON-RPC message to stdout (newline-delimited)."""
201
+ sys.stdout.write(json.dumps(msg, ensure_ascii=False, default=str) + "\n")
202
+ sys.stdout.flush()
203
+
204
+
205
+ def _notify(level: str, data: str) -> None:
206
+ """Send a log notification to the MCP client."""
207
+ _send({
208
+ "jsonrpc": "2.0",
209
+ "method": "notifications/message",
210
+ "params": {"level": level, "logger": "gitinstall", "data": data},
211
+ })
212
+
213
+
214
+ # ── Progress → MCP Notifications ─────────────────────────
215
+
216
+ def _progress_to_notification(event: dict) -> None:
217
+ """Convert install on_progress events to MCP log notifications."""
218
+ t = event.get("type", "")
219
+ if t == "detecting":
220
+ _notify("info", "Detecting system environment...")
221
+ elif t == "plan_ready":
222
+ n = len(event.get("steps", []))
223
+ _notify("info", f"Plan ready — {n} steps, confidence={event.get('confidence', '?')}")
224
+ elif t == "step_start":
225
+ _notify("info", f"[{event['step']}/{event['total']}] {event.get('description', event.get('command', ''))}")
226
+ elif t == "step_done":
227
+ _notify("info", f" ✓ Step {event['step']} done ({event.get('duration', 0):.1f}s)")
228
+ elif t == "step_failed":
229
+ _notify("warning", f" ✗ Step {event['step']} failed: {event.get('error', '')[:200]}")
230
+ elif t == "step_fixed":
231
+ _notify("info", f" ↻ Step {event['step']} auto-fixed")
232
+ elif t == "fallback_start":
233
+ _notify("info", f"Trying fallback strategy: {event.get('strategy', '?')}")
234
+ elif t == "install_done":
235
+ status = "succeeded" if event.get("success") else "failed"
236
+ _notify("info", f"Install {status} ({event.get('steps_completed', 0)}/{event.get('steps_total', 0)} steps)")
237
+
238
+
239
+ # ── Tool Execution ───────────────────────────────────────
240
+
241
+ def _call_tool(name: str, args: dict) -> tuple[list[dict], bool]:
242
+ """Execute a tool. Returns (content_list, is_error)."""
243
+ try:
244
+ if name == "detect_environment":
245
+ result = _detect()
246
+
247
+ elif name == "install_github_project":
248
+ result = _install(
249
+ args["project"],
250
+ install_dir=args.get("install_dir"),
251
+ dry_run=args.get("dry_run", False),
252
+ on_progress=_progress_to_notification,
253
+ )
254
+
255
+ elif name == "diagnose_install_error":
256
+ result = _diagnose(
257
+ args["stderr"],
258
+ command=args.get("command", ""),
259
+ stdout=args.get("stdout", ""),
260
+ )
261
+ if result is None:
262
+ return [{"type": "text", "text": "No matching error pattern found. The error may be project-specific."}], False
263
+
264
+ elif name == "get_project_info":
265
+ result = _fetch(args["project"])
266
+
267
+ elif name == "check_system_health":
268
+ result = _doctor()
269
+
270
+ elif name == "audit_dependencies":
271
+ result = _audit(
272
+ args["project"],
273
+ online=args.get("online", False),
274
+ )
275
+
276
+ else:
277
+ return [{"type": "text", "text": f"Unknown tool: {name}"}], True
278
+
279
+ text = json.dumps(result, indent=2, ensure_ascii=False, default=str)
280
+ return [{"type": "text", "text": text}], False
281
+
282
+ except Exception as e:
283
+ logger.exception(f"Tool '{name}' raised an exception")
284
+ return [{"type": "text", "text": f"Error executing {name}: {e}"}], True
285
+
286
+
287
+ # ── JSON-RPC Dispatch ────────────────────────────────────
288
+
289
+ def _handle(message: dict) -> dict | None:
290
+ """Process one JSON-RPC message. Returns response dict, or None for notifications."""
291
+ method = message.get("method")
292
+ msg_id = message.get("id")
293
+ params = message.get("params", {})
294
+
295
+ # ── Lifecycle ──
296
+
297
+ if method == "initialize":
298
+ return {
299
+ "jsonrpc": "2.0",
300
+ "id": msg_id,
301
+ "result": {
302
+ "protocolVersion": PROTOCOL_VERSION,
303
+ "capabilities": {"tools": {}, "logging": {}},
304
+ "serverInfo": {"name": "gitinstall", "version": __version__},
305
+ },
306
+ }
307
+
308
+ if method == "notifications/initialized":
309
+ logger.info("Client initialized")
310
+ return None
311
+
312
+ if method == "ping":
313
+ return {"jsonrpc": "2.0", "id": msg_id, "result": {}}
314
+
315
+ # ── Tools ──
316
+
317
+ if method == "tools/list":
318
+ return {
319
+ "jsonrpc": "2.0",
320
+ "id": msg_id,
321
+ "result": {"tools": TOOLS},
322
+ }
323
+
324
+ if method == "tools/call":
325
+ name = params.get("name", "")
326
+ arguments = params.get("arguments", {})
327
+ logger.info(f"→ {name}({json.dumps(arguments, ensure_ascii=False)[:200]})")
328
+
329
+ content, is_error = _call_tool(name, arguments)
330
+
331
+ result = {"content": content}
332
+ if is_error:
333
+ result["isError"] = True
334
+ return {"jsonrpc": "2.0", "id": msg_id, "result": result}
335
+
336
+ # ── Resources / Prompts (empty but respond correctly) ──
337
+
338
+ if method == "resources/list":
339
+ return {"jsonrpc": "2.0", "id": msg_id, "result": {"resources": []}}
340
+
341
+ if method == "prompts/list":
342
+ return {"jsonrpc": "2.0", "id": msg_id, "result": {"prompts": []}}
343
+
344
+ # ── Unknown method ──
345
+
346
+ if msg_id is not None:
347
+ return {
348
+ "jsonrpc": "2.0",
349
+ "id": msg_id,
350
+ "error": {"code": -32601, "message": f"Method not found: {method}"},
351
+ }
352
+
353
+ return None
354
+
355
+
356
+ # ── Main Loop ────────────────────────────────────────────
357
+
358
+ def serve() -> None:
359
+ """Read JSON-RPC messages from stdin, write responses to stdout."""
360
+ logger.info(f"gitinstall MCP server v{__version__} (protocol {PROTOCOL_VERSION})")
361
+
362
+ for line in sys.stdin:
363
+ line = line.strip()
364
+ if not line:
365
+ continue
366
+
367
+ try:
368
+ message = json.loads(line)
369
+ except json.JSONDecodeError as e:
370
+ logger.error(f"Invalid JSON: {e}")
371
+ _send({
372
+ "jsonrpc": "2.0",
373
+ "id": None,
374
+ "error": {"code": -32700, "message": f"Parse error: {e}"},
375
+ })
376
+ continue
377
+
378
+ response = _handle(message)
379
+ if response is not None:
380
+ _send(response)
381
+
382
+ logger.info("stdin closed — shutting down")
383
+
384
+
385
+ if __name__ == "__main__":
386
+ serve()