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
gitinstall/onboard.py ADDED
@@ -0,0 +1,276 @@
1
+ """
2
+ onboard.py - gitinstall 交互式引导向导
3
+ ========================================
4
+
5
+ 灵感来源:OpenClaw `openclaw onboard` 交互式设置流程
6
+
7
+ 功能:
8
+ 1. 首次使用引导:检测环境 → 配置推荐 → API Key 设置
9
+ 2. 自动生成 ~/.gitinstall/config.json 配置文件
10
+ 3. 初始化内建 Skills
11
+ 4. 可选:安装示例项目验证环境
12
+
13
+ 零外部依赖,纯 Python 标准库。
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import json
19
+ import os
20
+ import sys
21
+ import time
22
+ from pathlib import Path
23
+
24
+
25
+ # ── 配置文件路径 ──
26
+ CONFIG_DIR = Path.home() / ".gitinstall"
27
+ CONFIG_FILE = CONFIG_DIR / "config.json"
28
+
29
+
30
+ def _input_with_default(prompt: str, default: str = "") -> str:
31
+ """带默认值的输入"""
32
+ if default:
33
+ display = f"{prompt} [{default}]: "
34
+ else:
35
+ display = f"{prompt}: "
36
+ try:
37
+ val = input(display).strip()
38
+ return val if val else default
39
+ except (EOFError, KeyboardInterrupt):
40
+ print()
41
+ return default
42
+
43
+
44
+ def _yes_no(prompt: str, default: bool = True) -> bool:
45
+ """是/否确认"""
46
+ hint = "Y/n" if default else "y/N"
47
+ try:
48
+ val = input(f"{prompt} ({hint}): ").strip().lower()
49
+ if not val:
50
+ return default
51
+ return val in ("y", "yes", "是")
52
+ except (EOFError, KeyboardInterrupt):
53
+ print()
54
+ return default
55
+
56
+
57
+ def _step_header(step: int, total: int, title: str):
58
+ """步骤标题"""
59
+ print(f"\n{'─' * 50}")
60
+ print(f" 📍 步骤 {step}/{total}: {title}")
61
+ print(f"{'─' * 50}")
62
+
63
+
64
+ def run_onboard():
65
+ """运行交互式引导向导"""
66
+ total_steps = 5
67
+
68
+ print()
69
+ print("🦀 欢迎使用 gitinstall!")
70
+ print("═" * 50)
71
+ print(" 一句话安装任何 GitHub/GitLab/Bitbucket/Gitee 开源项目")
72
+ print(" 本向导将帮助您完成初始配置")
73
+ print("═" * 50)
74
+
75
+ config = {}
76
+
77
+ # ──────────── 步骤 1:环境检测 ────────────
78
+ _step_header(1, total_steps, "环境检测")
79
+ print(" 正在检测您的系统环境...")
80
+ print()
81
+
82
+ try:
83
+ from detector import EnvironmentDetector
84
+ env = EnvironmentDetector().detect()
85
+
86
+ os_info = env.get("os", {})
87
+ gpu_info = env.get("gpu", {})
88
+ runtimes = env.get("runtimes", {})
89
+
90
+ print(f" 🖥️ 系统: {os_info.get('type', '?')} {os_info.get('version', '')}")
91
+ print(f" 🏗️ 架构: {os_info.get('arch', '?')}")
92
+ if os_info.get("chip"):
93
+ print(f" 💻 芯片: {os_info['chip']}")
94
+ if gpu_info.get("name"):
95
+ print(f" 🎮 GPU: {gpu_info['name']} ({gpu_info.get('type', '')})")
96
+ else:
97
+ print(f" 🎮 GPU: 仅 CPU")
98
+
99
+ # 检测已有工具
100
+ tools_found = []
101
+ for name, info in runtimes.items():
102
+ if isinstance(info, dict) and info.get("available"):
103
+ ver = info.get("version", "")
104
+ tools_found.append(f"{name} {ver}".strip())
105
+ if tools_found:
106
+ print(f" 🔧 已安装: {', '.join(tools_found[:8])}")
107
+
108
+ config["detected_env"] = {
109
+ "os": os_info.get("type"),
110
+ "arch": os_info.get("arch"),
111
+ "gpu": gpu_info.get("type"),
112
+ }
113
+ except Exception as e:
114
+ print(f" ⚠️ 环境检测出错: {e}")
115
+ print(" (不影响使用,继续配置)")
116
+
117
+ print("\n ✅ 环境检测完成!")
118
+
119
+ # ──────────── 步骤 2:API Key 配置 ────────────
120
+ _step_header(2, total_steps, "API Key 配置(可选)")
121
+ print(" gitinstall 无需任何 API Key 即可工作!")
122
+ print(" 内置 SmartPlanner 覆盖 80+ 已知项目 + 50+ 语言模板")
123
+ print()
124
+ print(" 可选增强:")
125
+ print(" • GITHUB_TOKEN → 提升 API 配额 60→5000 次/小时")
126
+ print(" • LLM API Key → AI 增强安装计划")
127
+ print()
128
+
129
+ if _yes_no(" 是否现在配置 GITHUB_TOKEN?", default=False):
130
+ token = _input_with_default(" 输入 GitHub Token (ghp_...)")
131
+ if token:
132
+ config["github_token"] = token
133
+ print(" ✅ 已记录(将写入配置文件,不写入 shell profile)")
134
+
135
+ print()
136
+ if _yes_no(" 是否配置 LLM API Key?(用于 AI 增强安装分析)", default=False):
137
+ print()
138
+ print(" 支持的 LLM 服务:")
139
+ print(" 1. Anthropic Claude (推荐)")
140
+ print(" 2. OpenAI GPT")
141
+ print(" 3. Groq Llama 3.3 (免费)")
142
+ print(" 4. DeepSeek (便宜)")
143
+ print(" 5. Google Gemini")
144
+ print(" 6. 本地 Ollama (完全免费)")
145
+ print(" 7. 跳过")
146
+ print()
147
+ choice = _input_with_default(" 选择 (1-7)", "7")
148
+
149
+ key_map = {
150
+ "1": ("ANTHROPIC_API_KEY", "Anthropic API Key (sk-ant-...)"),
151
+ "2": ("OPENAI_API_KEY", "OpenAI API Key (sk-...)"),
152
+ "3": ("GROQ_API_KEY", "Groq API Key (gsk_...)"),
153
+ "4": ("DEEPSEEK_API_KEY", "DeepSeek API Key"),
154
+ "5": ("GEMINI_API_KEY", "Google Gemini API Key"),
155
+ }
156
+
157
+ if choice in key_map:
158
+ env_var, prompt = key_map[choice]
159
+ key_val = _input_with_default(f" 输入 {prompt}")
160
+ if key_val:
161
+ config["llm_key"] = {env_var: key_val}
162
+ print(f" ✅ 已记录 {env_var}")
163
+ elif choice == "6":
164
+ print(" ℹ️ 确保 Ollama 运行中: ollama serve")
165
+ print(" gitinstall 会自动检测 localhost:11434")
166
+ config["llm_preference"] = "ollama"
167
+ else:
168
+ print(" ⏭️ 跳过 LLM 配置")
169
+
170
+ # ──────────── 步骤 3:安装偏好 ────────────
171
+ _step_header(3, total_steps, "安装偏好")
172
+
173
+ print(" 默认安装目录:")
174
+ install_dir = _input_with_default(" 项目安装目录", "~/projects")
175
+ config["default_install_dir"] = install_dir
176
+
177
+ print()
178
+ print(" 安装模式:")
179
+ print(" 1. 安全模式(默认)— 危险命令需确认")
180
+ print(" 2. 快速模式 — 自动跳过确认(信任所有项目)")
181
+ print(" 3. 严格模式 — 仅允许已知安全项目")
182
+ mode = _input_with_default(" 选择 (1-3)", "1")
183
+ config["install_mode"] = {"1": "safe", "2": "fast", "3": "strict"}.get(mode, "safe")
184
+
185
+ print()
186
+ if _yes_no(" 是否启用安装遥测?(匿名统计,帮助改进 SmartPlanner)", default=True):
187
+ config["telemetry"] = True
188
+ else:
189
+ config["telemetry"] = False
190
+
191
+ # ──────────── 步骤 4:初始化 Skills ────────────
192
+ _step_header(4, total_steps, "Skills 插件初始化")
193
+ print(" Skills 是社区贡献的安装策略扩展")
194
+ print(" 内建 Skills: auto-venv, docker-prefer, gpu-optimizer, batch-install 等")
195
+ print()
196
+
197
+ if _yes_no(" 是否初始化内建 Skills?", default=True):
198
+ try:
199
+ from skills import ensure_builtin_skills, SkillManager
200
+ ensure_builtin_skills()
201
+ mgr = SkillManager()
202
+ skills = mgr.list_skills()
203
+ print(f" ✅ 已初始化 {len(skills)} 个内建 Skills")
204
+ config["skills_initialized"] = True
205
+ except Exception as e:
206
+ print(f" ⚠️ Skills 初始化失败: {e}")
207
+ else:
208
+ print(" ⏭️ 跳过(稍后可用 'gitinstall skills init' 初始化)")
209
+
210
+ # ──────────── 步骤 5:保存 + 验证 ────────────
211
+ _step_header(5, total_steps, "保存配置")
212
+
213
+ # 保存配置
214
+ config["version"] = "1.0"
215
+ config["onboard_completed"] = True
216
+ config["onboard_time"] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
217
+
218
+ CONFIG_DIR.mkdir(parents=True, exist_ok=True)
219
+ with open(CONFIG_FILE, "w", encoding="utf-8") as f:
220
+ json.dump(config, f, indent=2, ensure_ascii=False)
221
+
222
+ # 安全权限
223
+ try:
224
+ os.chmod(CONFIG_DIR, 0o700)
225
+ os.chmod(CONFIG_FILE, 0o600)
226
+ except OSError:
227
+ pass
228
+
229
+ print(f" ✅ 配置已保存到 {CONFIG_FILE}")
230
+
231
+ # 验证安装
232
+ print()
233
+ if _yes_no(" 是否运行 Doctor 诊断验证环境?", default=True):
234
+ try:
235
+ from doctor import run_doctor, format_doctor_report
236
+ report = run_doctor()
237
+ print(format_doctor_report(report))
238
+ except Exception as e:
239
+ print(f" ⚠️ 诊断失败: {e}")
240
+
241
+ # 完成
242
+ print()
243
+ print("🎉 配置完成!")
244
+ print("═" * 50)
245
+ print()
246
+ print(" 快速开始:")
247
+ print(" gitinstall install comfyanonymous/ComfyUI # 安装项目")
248
+ print(" gitinstall plan pytorch/pytorch # 查看安装计划")
249
+ print(" gitinstall doctor # 诊断系统")
250
+ print(" gitinstall skills list # 查看 Skills")
251
+ print(" gitinstall web # 启动 Web UI")
252
+ print()
253
+ print(" 支持多平台:")
254
+ print(" gitinstall install gitlab.com/user/repo # GitLab")
255
+ print(" gitinstall install gitee.com/user/repo # Gitee 国内")
256
+ print(" gitinstall install bitbucket.org/user/repo # Bitbucket")
257
+ print()
258
+ print(" 文档:https://github.com/yourusername/gitinstall")
259
+ print()
260
+
261
+
262
+ def load_config() -> dict:
263
+ """加载用户配置(如无则返回空 dict)"""
264
+ if CONFIG_FILE.exists():
265
+ try:
266
+ with open(CONFIG_FILE, encoding="utf-8") as f:
267
+ return json.load(f)
268
+ except (json.JSONDecodeError, OSError):
269
+ pass
270
+ return {}
271
+
272
+
273
+ def is_first_run() -> bool:
274
+ """检查是否首次运行"""
275
+ config = load_config()
276
+ return not config.get("onboard_completed", False)
gitinstall/planner.py ADDED
@@ -0,0 +1,222 @@
1
+ """
2
+ planner.py - 智能安装计划生成器(零 AI、零 API Key)
3
+ ======================================================
4
+
5
+ 核心设计原则:
6
+ 不是所有人都有 AI Key,也不是所有人都能跑本地大模型。
7
+ 本模块在完全没有任何 LLM 的情况下,生成高质量的安装计划。
8
+
9
+ 三层生成策略(按质量递减):
10
+ 1. 已知项目数据库 ── 精确匹配 80+ 热门项目,质量最高(confidence=high)
11
+ 2. 依赖文件类型模板 ── Python/Node/Docker/Rust/Go,GPU/平台自适应(confidence=medium)
12
+ 3. README 规则提取 ── 从代码块提取命令,保底可用(confidence=low)
13
+
14
+ 代码组织(拆分为四个模块):
15
+ planner.py → SmartPlanner 主类 + 路由逻辑(本文件)
16
+ planner_helpers.py → 平台适配辅助函数(纯函数,无状态)
17
+ planner_known_projects.py → 已知项目数据库
18
+ planner_templates.py → 各语言安装模板(Mixin 类)
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import re
24
+ import json
25
+ import sys
26
+ from pathlib import Path
27
+ from typing import Any
28
+
29
+ # 将 tools 目录加入路径(兼容 from tools.planner 和直接执行)
30
+ _THIS_DIR = str(Path(__file__).parent)
31
+ if _THIS_DIR not in sys.path:
32
+ sys.path.insert(0, _THIS_DIR)
33
+
34
+ # 导入拆分出去的模块
35
+ from planner_helpers import (
36
+ _os_type, _is_apple_silicon, _gpu_type, _cuda_major,
37
+ _has_pm, _has_runtime, _python_cmd, _pip_cmd,
38
+ _venv_activate, _dep_names, _dep_content,
39
+ _is_maturin_project, _preferred_java_version,
40
+ _has_haskell_cabal_file, _stack_resolver, _stack_lts_major,
41
+ _zig_minimum_version, _zig_fallback_version, _version_tuple,
42
+ _zig_uses_legacy_build_api, _haskell_system_packages,
43
+ _haskell_macos_env_prefix, _haskell_repo_template,
44
+ _torch_install_cmd, _node_pm,
45
+ _make_step, _get_gpu_name,
46
+ )
47
+ from planner_known_projects import _KNOWN_PROJECTS
48
+ from planner_templates import PlanTemplateMixin
49
+
50
+
51
+ class SmartPlanner(PlanTemplateMixin):
52
+ """
53
+ 零 AI、零 API Key 的智能安装计划生成器。
54
+
55
+ 设计目标:
56
+ - 没有 AI Key → 依然可以生成正确的安装步骤
57
+ - 没有本地大模型 → 依然可以自动适配 GPU 和操作系统
58
+ - 没有网络 → 使用缓存的已知项目知识库
59
+ """
60
+
61
+ def generate_plan(
62
+ self,
63
+ owner: str,
64
+ repo: str,
65
+ env: dict,
66
+ project_types: list[str],
67
+ dependency_files: dict[str, str],
68
+ readme: str,
69
+ clone_url: str = "",
70
+ source_path: str = "",
71
+ ) -> dict[str, Any]:
72
+ """
73
+ 生成完整安装计划,不依赖任何外部 AI 服务。
74
+
75
+ Args:
76
+ clone_url: 自定义 clone URL(非 GitHub 项目时使用)
77
+ source_path: 本地路径(已存在于文件系统的项目,跳过 git clone)
78
+
79
+ Returns:
80
+ {
81
+ "project_name": str,
82
+ "steps": [{"command": str, "description": str, "_warning": str}, ...],
83
+ "launch_command": str,
84
+ "notes": str,
85
+ "confidence": "high" | "medium" | "low",
86
+ "strategy": "known_project" | "type_template_*" | "readme_extract",
87
+ "mode": "smart_planner",
88
+ }
89
+ """
90
+ # 设置源信息(供模板方法使用)
91
+ self._clone_url = clone_url
92
+ self._source_path = source_path
93
+
94
+ key = f"{owner}/{repo}".lower()
95
+
96
+ # ── 策略 1:已知项目精确匹配(本地路径跳过,因为可能是私有项目) ──
97
+ if not source_path and key in _KNOWN_PROJECTS:
98
+ plan = self._plan_from_known(key, env)
99
+ plan.update({"confidence": "high", "strategy": "known_project", "mode": "smart_planner"})
100
+ return plan
101
+
102
+ # ── 策略 2:依赖文件类型模板 ─────────────────
103
+ # 优先级设计原则:
104
+ # - 语言特有的信号(如 composer.json=PHP)比通用文件(如 package.json、Dockerfile)更准确
105
+ # - 很多项目同时有 Dockerfile(容器化)和 package.json(前端)但不是 Docker/Node 项目
106
+ # - environment.yml 可能只是 CI 配置(如 redis),不代表是 conda 项目
107
+ types = set(project_types)
108
+ dep_keys = _dep_names(dependency_files)
109
+
110
+ # ── 优先级设计原则 ──────────────────────────────────────
111
+ # 1. 语言特有信号(如 mix.exs=Elixir, stack.yaml=Haskell)比通用文件更准确
112
+ # 2. 通用文件(Makefile, package.json, Dockerfile)几乎所有项目都可能有
113
+ # 3. 语言类型检测必须优先于通用构建系统
114
+ # 4. 排除规则:当多类型共存时,选择最"核心"的那个
115
+ # ──────────────────────────────────────────────────────
116
+
117
+ # conda:仅当确实是 Python 项目 + environment.yml 是唯一构建描述
118
+ if ("python" in types
119
+ and "environment.yml" in dep_keys
120
+ and not (dep_keys & {"requirements.txt", "setup.py", "pyproject.toml", "setup.cfg", "Pipfile"})):
121
+ plan = self._plan_conda(owner, repo, env)
122
+ # docker:仅当 docker 是唯一的核心技术(排除所有语言项目)
123
+ elif "docker" in types and not (types & {
124
+ "python", "node", "dotnet", "java", "go", "rust",
125
+ "ruby", "php", "swift", "c", "cpp", "cmake", "make",
126
+ "kotlin", "scala", "dart", "elixir", "haskell", "lua",
127
+ "perl", "r", "julia", "zig", "clojure", "meson", "shell",
128
+ }):
129
+ plan = self._plan_docker(owner, repo, env)
130
+ # ML 框架(最强信号)
131
+ elif types & {"pytorch", "diffusers", "tensorflow", "gradio"}:
132
+ plan = self._plan_python_ml(owner, repo, env, dep_keys, types)
133
+
134
+ # ── 语言特有依赖文件检测(必须在通用 Makefile/package.json 之前) ──
135
+ # Elixir (mix.exs 是唯一标识,比 Makefile/package.json 更准确)
136
+ elif "elixir" in types or "mix.exs" in dep_keys:
137
+ plan = self._plan_elixir(owner, repo, env)
138
+ # Haskell (stack.yaml / .cabal 文件)
139
+ elif "haskell" in types or "stack.yaml" in dep_keys or any(f.endswith(".cabal") for f in dep_keys):
140
+ plan = self._plan_haskell(owner, repo, env, dependency_files)
141
+ # Scala (build.sbt 是唯一标识)
142
+ elif "scala" in types or "build.sbt" in dep_keys:
143
+ plan = self._plan_scala(owner, repo, env)
144
+ # Dart / Flutter (pubspec.yaml 是唯一标识)
145
+ elif "dart" in types or "pubspec.yaml" in dep_keys:
146
+ plan = self._plan_dart(owner, repo, env)
147
+ # Clojure (project.clj / deps.edn)
148
+ elif "clojure" in types or "project.clj" in dep_keys:
149
+ plan = self._plan_clojure(owner, repo, env)
150
+ # Julia (Project.toml)
151
+ elif "julia" in types or "Project.toml" in dep_keys:
152
+ plan = self._plan_julia(owner, repo, env)
153
+ # Zig (build.zig)
154
+ elif "zig" in types or "build.zig" in dep_keys:
155
+ plan = self._plan_zig(owner, repo, env, dependency_files)
156
+ # Lua(语言检测跳过含 Makefile 的多类型项目时用类型字段判断)
157
+ elif "lua" in types and not (types & {"python", "node", "go", "rust", "java"}):
158
+ plan = self._plan_lua(owner, repo, env)
159
+ # Perl(语言检测为 perl 且无更强信号)
160
+ elif "perl" in types and not (types & {"python", "node", "go", "rust", "java"}):
161
+ plan = self._plan_perl(owner, repo, env)
162
+ # R
163
+ elif "r" in types and not (types & {"python", "node"}):
164
+ plan = self._plan_r(owner, repo, env)
165
+
166
+ # ── 主流语言(通用构建文件) ──
167
+ # Python(跳过 Rust+Python 混合 / C++项目含 Python 绑定如 grpc)
168
+ elif _is_maturin_project(types, dependency_files):
169
+ plan = self._plan_python_rust_package(owner, repo, env)
170
+ elif ("python" in types or dep_keys & {"requirements.txt", "setup.py", "pyproject.toml"}) and "rust" not in types and not (types & {"c", "cpp", "cmake"}):
171
+ plan = self._plan_python(owner, repo, env, dep_keys)
172
+ # PHP(优先于 Node——PHP 项目常有 package.json 做前端资源)
173
+ elif "php" in types or "composer.json" in dep_keys:
174
+ plan = self._plan_php(owner, repo, env)
175
+ # .NET(优先于 Docker——.NET 项目常有 Dockerfile)
176
+ elif "dotnet" in types:
177
+ plan = self._plan_dotnet(owner, repo, env)
178
+ # Swift(排除 C/C++ 库提供 Package.swift 做 SPM 兼容的情况)
179
+ elif "swift" in types and not (types & {"cpp", "c", "cmake"}):
180
+ plan = self._plan_swift(owner, repo, env)
181
+ # Kotlin(独立 Kotlin 项目,非 Java 混合)
182
+ elif "kotlin" in types and not (types & {"java"}):
183
+ plan = self._plan_kotlin(owner, repo, env)
184
+ # Node.js(排除 Ruby/Elixir/Shell 项目的前端或测试 package.json)
185
+ elif ("node" in types or dep_keys & {"package.json"}) and not (types & {"ruby", "elixir", "lua", "perl", "shell", "c", "cpp", "cmake", "go", "java", "rust"}):
186
+ plan = self._plan_node(owner, repo, env)
187
+ # Rust(排除 C/C++ 项目中的辅助 Cargo.toml)
188
+ elif ("rust" in types or "Cargo.toml" in dep_keys) and not (types & {"c", "cpp"}):
189
+ plan = self._plan_rust(owner, repo, env)
190
+ # Go
191
+ elif "go" in types or "go.mod" in dep_keys:
192
+ plan = self._plan_go(owner, repo, env, dep_keys)
193
+ # CMake
194
+ elif "cmake" in types or "CMakeLists.txt" in dep_keys:
195
+ plan = self._plan_cmake(owner, repo, env)
196
+ # Java
197
+ elif "java" in types or dep_keys & {"pom.xml", "build.gradle", "build.gradle.kts"}:
198
+ plan = self._plan_java(owner, repo, env, dep_keys, readme)
199
+ # Makefile/autotools(跳过 shell 脚本项目——其 Makefile 只是安装/测试辅助)
200
+ elif ("make" in types or "autotools" in types or dep_keys & {"Makefile", "configure", "configure.ac", "Makefile.am"}) and not ("shell" in types and not (types & {"c", "cpp", "cmake"})) and not (types & {"ruby", "python", "node", "go", "php"}):
201
+ plan = self._plan_make(owner, repo, env, dep_keys)
202
+ # Meson 构建系统(在 cmake/make 之后——meson.build 常作为替代构建系统)
203
+ elif "meson" in types or "meson.build" in dep_keys:
204
+ plan = self._plan_meson(owner, repo, env)
205
+ # Ruby(Gemfile 经常是辅助工具而非主项目)
206
+ elif "ruby" in types or "Gemfile" in dep_keys:
207
+ plan = self._plan_ruby(owner, repo, env)
208
+ # Shell 脚本项目(语言检测到 shell,允许含 Makefile/package.json 的项目)
209
+ elif "shell" in types and not (types & {"python", "go", "rust", "java", "cmake", "c", "cpp"}):
210
+ plan = self._plan_shell(owner, repo, env)
211
+ # PlatformIO / Arduino 嵌入式项目
212
+ elif "platformio" in types or "arduino" in types:
213
+ plan = self._plan_platformio(owner, repo, env)
214
+ # C/C++ 通用保底(没有 CMakeLists.txt / Makefile 等构建文件的纯 C/C++ 项目)
215
+ elif types & {"c", "cpp"}:
216
+ plan = self._plan_c_cpp(owner, repo, env)
217
+ else:
218
+ # ── 策略 3:README 规则提取(保底) ──────
219
+ plan = self._plan_from_readme(owner, repo, readme, project_types=project_types)
220
+
221
+ plan["mode"] = "smart_planner"
222
+ return plan