multi-lang-build 0.2.0__py3-none-any.whl → 0.2.2__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.
@@ -7,7 +7,7 @@ from multi_lang_build.compiler.python import PythonCompiler
7
7
  from multi_lang_build.mirror.config import MirrorConfig, get_mirror_config
8
8
  from multi_lang_build.register import register_skill
9
9
 
10
- __version__ = "0.2.0"
10
+ __version__ = "0.2.1"
11
11
  __all__ = [
12
12
  "BuildConfig",
13
13
  "BuildResult",
@@ -55,3 +55,10 @@ def main_register() -> None:
55
55
  args = parser.parse_args()
56
56
  success = register_skill(args.ide)
57
57
  sys.exit(0 if success else 1)
58
+
59
+
60
+ def main_mirror() -> None:
61
+ """Entry point for the mirror CLI tool."""
62
+ from multi_lang_build.mirror.cli import mirror_main
63
+
64
+ mirror_main()
multi_lang_build/cli.py CHANGED
@@ -92,12 +92,54 @@ Supported languages:
92
92
  mirror_parser = subparsers.add_parser(
93
93
  "mirror",
94
94
  help="Manage mirror configurations",
95
- description="List or configure mirror settings",
95
+ description="List or configure domestic mirror acceleration settings",
96
+ epilog="""
97
+ Examples:
98
+ %(prog)s mirror list List all available mirrors
99
+ %(prog)s mirror set pip Configure pip mirror (default)
100
+ %(prog)s mirror set go Configure go proxy mirror
101
+ %(prog)s mirror set pnpm Configure pnpm registry mirror
102
+ %(prog)s mirror show Show current configuration
103
+ %(prog)s mirror reset Reset configuration
104
+ """,
96
105
  )
97
106
  mirror_subparsers = mirror_parser.add_subparsers(dest="mirror_action")
98
107
 
99
108
  list_parser = mirror_subparsers.add_parser("list", help="List available mirrors")
100
- list_parser.add_argument("--language", type=str, help="Filter by language")
109
+ list_parser.add_argument("--json", action="store_true", help="Output as JSON")
110
+
111
+ set_parser = mirror_subparsers.add_parser("set", help="Set mirror configuration")
112
+ set_parser.add_argument(
113
+ "type",
114
+ type=str,
115
+ default="pip",
116
+ nargs="?",
117
+ choices=["pip", "go", "npm", "pnpm"],
118
+ help="Package manager type (default: pip)",
119
+ )
120
+ set_parser.add_argument(
121
+ "mirror",
122
+ type=str,
123
+ nargs="?",
124
+ default="pip",
125
+ help="Mirror to use",
126
+ )
127
+
128
+ show_parser = mirror_subparsers.add_parser("show", help="Show current configuration")
129
+ show_parser.add_argument(
130
+ "--global",
131
+ dest="global_level",
132
+ action="store_true",
133
+ help="Show global config instead of project-level",
134
+ )
135
+
136
+ reset_parser = mirror_subparsers.add_parser("reset", help="Reset configuration")
137
+ reset_parser.add_argument(
138
+ "--global",
139
+ dest="global_level",
140
+ action="store_true",
141
+ help="Reset global config instead of project-level",
142
+ )
101
143
 
102
144
  # Register subcommand for IDE integration
103
145
  register_parser = subparsers.add_parser(
@@ -210,18 +252,33 @@ Examples:
210
252
  )
211
253
 
212
254
  elif parsed_args.language == "mirror":
213
- if parsed_args.mirror_action == "list":
214
- from multi_lang_build.mirror.config import get_all_mirror_names, get_mirror_config
255
+ from multi_lang_build.mirror.cli import configure_mirror, print_mirrors, get_current_config
215
256
 
216
- mirrors = get_all_mirror_names()
217
- if parsed_args.language:
218
- mirrors = [m for m in mirrors if m in parsed_args.language.split(",")]
219
-
220
- print("Available mirrors:")
221
- for mirror_name in sorted(mirrors):
222
- config = get_mirror_config(mirror_name)
223
- if config:
224
- print(f" {mirror_name}: {config['url']}")
257
+ if parsed_args.mirror_action == "list":
258
+ if getattr(parsed_args, "json", False):
259
+ from multi_lang_build.mirror.cli import get_all_mirrors
260
+ import json
261
+ print(json.dumps(get_all_mirrors(), indent=2, ensure_ascii=False))
262
+ else:
263
+ print_mirrors()
264
+ elif parsed_args.mirror_action == "set":
265
+ mirror_type = getattr(parsed_args, "type", "pip")
266
+ mirror_key = getattr(parsed_args, "mirror", "pip")
267
+ success = configure_mirror(mirror_type, mirror_key)
268
+ sys.exit(0 if success else 1)
269
+ elif parsed_args.mirror_action == "show":
270
+ config = get_current_config()
271
+ if config:
272
+ print("\n📦 Current Mirror Configuration:")
273
+ print("-" * 40)
274
+ for key, value in config.items():
275
+ print(f" • {key}: {value}")
276
+ print()
277
+ else:
278
+ print("\n📦 No mirror configured")
279
+ print(" Run 'multi-lang-build mirror set <type> <mirror>' to configure")
280
+ print()
281
+ print_mirrors()
225
282
  else:
226
283
  mirror_parser.print_help()
227
284
 
@@ -0,0 +1,340 @@
1
+ """Mirror configuration CLI for domestic acceleration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import subprocess
8
+ import sys
9
+ from pathlib import Path
10
+ from typing import Literal
11
+
12
+
13
+ def get_all_mirrors() -> dict:
14
+ """Get all available mirrors with their configurations."""
15
+ from multi_lang_build.mirror.config import MIRROR_CONFIGS
16
+
17
+ mirrors = {}
18
+ for key, config in MIRROR_CONFIGS.items():
19
+ mirrors[key] = {
20
+ "name": config["name"],
21
+ "url": config["url"],
22
+ }
23
+ return mirrors
24
+
25
+
26
+ def print_mirrors() -> None:
27
+ """Print all available mirrors."""
28
+ mirrors = get_all_mirrors()
29
+
30
+ print("\n📦 Available Mirrors:")
31
+ print("-" * 60)
32
+
33
+ # Group by category
34
+ categories = {
35
+ "js": ["npm", "pnpm", "yarn"],
36
+ "go": ["go", "go_qiniu", "go_vip"],
37
+ "python": ["pip", "pip_aliyun", "pip_douban", "pip_huawei", "python", "poetry"],
38
+ }
39
+
40
+ category_names = {
41
+ "js": "🌐 JavaScript (npm/pnpm/yarn)",
42
+ "go": "🔷 Go",
43
+ "python": "🐍 Python (pip/PyPI)",
44
+ }
45
+
46
+ for cat_key, cat_mirrors in categories.items():
47
+ print(f"\n{category_names.get(cat_key, cat_key)}")
48
+ for mirror_key in cat_mirrors:
49
+ if mirror_key in mirrors:
50
+ config = mirrors[mirror_key]
51
+ print(f" • {mirror_key:12} - {config['name']}")
52
+ print(f" {config['url']}")
53
+
54
+ print()
55
+
56
+
57
+ def configure_pip(mirror_key: str) -> bool:
58
+ """Configure pip mirror using pip config command."""
59
+ from multi_lang_build.mirror.config import get_mirror_config
60
+
61
+ config = get_mirror_config(mirror_key)
62
+ if config is None:
63
+ print(f"❌ Unknown pip mirror: {mirror_key}")
64
+ return False
65
+
66
+ url = config["url"]
67
+ if not url.endswith("/simple") and not url.endswith("/simple/"):
68
+ url = f"{url}/simple"
69
+
70
+ # Try pip3 first, fallback to pip
71
+ pip_cmd = "pip3" if sys.platform != "win32" else "pip"
72
+
73
+ try:
74
+ subprocess.run(
75
+ [pip_cmd, "config", "set", "global.index-url", url],
76
+ check=True,
77
+ )
78
+ print(f"✅ pip mirror set to: {url}")
79
+ return True
80
+ except subprocess.CalledProcessError as e:
81
+ print(f"❌ Failed to set pip mirror: {e}")
82
+ return False
83
+ except FileNotFoundError:
84
+ # Try alternative
85
+ try:
86
+ alternative = "pip" if pip_cmd == "pip3" else "pip3"
87
+ subprocess.run(
88
+ [alternative, "config", "set", "global.index-url", url],
89
+ check=True,
90
+ )
91
+ print(f"✅ pip mirror set to: {url}")
92
+ return True
93
+ except Exception:
94
+ print("❌ pip/pip3 not found")
95
+ return False
96
+
97
+
98
+ def configure_go(mirror_key: str) -> bool:
99
+ """Configure Go proxy using go env -w."""
100
+ from multi_lang_build.mirror.config import get_mirror_config
101
+
102
+ config = get_mirror_config(mirror_key)
103
+ if config is None:
104
+ print(f"❌ Unknown Go mirror: {mirror_key}")
105
+ return False
106
+
107
+ # Get the GOPROXY value from environment variables
108
+ proxy_value = config["environment_variables"].get("GOPROXY", f"{config['url']},direct")
109
+
110
+ try:
111
+ subprocess.run(
112
+ ["go", "env", "-w", f"GOPROXY={proxy_value}"],
113
+ check=True,
114
+ )
115
+ print(f"✅ Go GOPROXY set to: {proxy_value}")
116
+
117
+ # Also set GOSUMDB if present
118
+ gosumdb = config["environment_variables"].get("GOSUMDB")
119
+ if gosumdb:
120
+ subprocess.run(
121
+ ["go", "env", "-w", f"GOSUMDB={gosumdb}"],
122
+ check=True,
123
+ )
124
+ print(f"✅ Go GOSUMDB set to: {gosumdb}")
125
+
126
+ return True
127
+ except subprocess.CalledProcessError as e:
128
+ print(f"❌ Failed to set Go mirror: {e}")
129
+ return False
130
+ except FileNotFoundError:
131
+ print("❌ go not found")
132
+ return False
133
+
134
+
135
+ def configure_npm(mirror_key: str) -> bool:
136
+ """Configure npm/pnpm registry."""
137
+ from multi_lang_build.mirror.config import get_mirror_config
138
+
139
+ config = get_mirror_config(mirror_key)
140
+ if config is None:
141
+ print(f"❌ Unknown npm mirror: {mirror_key}")
142
+ return False
143
+
144
+ url = config["url"]
145
+
146
+ try:
147
+ subprocess.run(
148
+ ["npm", "config", "set", "registry", url],
149
+ check=True,
150
+ )
151
+ print(f"✅ npm registry set to: {url}")
152
+
153
+ # Also set for pnpm if available
154
+ try:
155
+ subprocess.run(
156
+ ["pnpm", "config", "set", "registry", url],
157
+ check=True,
158
+ )
159
+ print(f"✅ pnpm registry set to: {url}")
160
+ except (subprocess.CalledProcessError, FileNotFoundError):
161
+ pass
162
+
163
+ return True
164
+ except subprocess.CalledProcessError as e:
165
+ print(f"❌ Failed to set npm registry: {e}")
166
+ return False
167
+ except FileNotFoundError:
168
+ print("❌ npm not found")
169
+ return False
170
+
171
+
172
+ def get_current_config() -> dict:
173
+ """Get current mirror configuration."""
174
+ config = {}
175
+
176
+ # Try pip3 first, fallback to pip
177
+ pip_cmd = "pip3" if sys.platform != "win32" else "pip"
178
+
179
+ try:
180
+ result = subprocess.run(
181
+ [pip_cmd, "config", "get", "global.index-url"],
182
+ capture_output=True,
183
+ text=True,
184
+ )
185
+ if result.returncode == 0 and result.stdout.strip():
186
+ config["pip"] = result.stdout.strip()
187
+ except Exception:
188
+ pass
189
+
190
+ # Check Go
191
+ try:
192
+ result = subprocess.run(
193
+ ["go", "env", "GOPROXY"],
194
+ capture_output=True,
195
+ text=True,
196
+ )
197
+ if result.returncode == 0:
198
+ config["go"] = result.stdout.strip()
199
+ except Exception:
200
+ pass
201
+
202
+ # Check npm
203
+ try:
204
+ result = subprocess.run(
205
+ ["npm", "config", "get", "registry"],
206
+ capture_output=True,
207
+ text=True,
208
+ )
209
+ if result.returncode == 0:
210
+ config["npm"] = result.stdout.strip()
211
+ except Exception:
212
+ pass
213
+
214
+ return config
215
+
216
+
217
+ def configure_mirror(mirror_type: str, mirror_key: str) -> bool:
218
+ """Configure mirror for a specific package manager.
219
+
220
+ Args:
221
+ mirror_type: Package manager type (pip, go, npm)
222
+ mirror_key: Mirror key to use
223
+
224
+ Returns:
225
+ True if successful, False otherwise
226
+ """
227
+ if mirror_type == "pip":
228
+ return configure_pip(mirror_key)
229
+ elif mirror_type == "go":
230
+ return configure_go(mirror_key)
231
+ elif mirror_type in ["npm", "pnpm"]:
232
+ return configure_npm(mirror_key)
233
+ else:
234
+ print(f"❌ Unknown package manager: {mirror_type}")
235
+ return False
236
+
237
+
238
+ def mirror_main():
239
+ """Main entry point for mirror command."""
240
+ import argparse
241
+
242
+ parser = argparse.ArgumentParser(
243
+ description="Configure domestic mirror acceleration",
244
+ epilog="""
245
+ Examples:
246
+ %(prog)s list List all available mirrors
247
+ %(prog)s set pip Configure pip mirror (default)
248
+ %(prog)s set go Configure Go proxy
249
+ %(prog)s set npm Configure npm registry
250
+ %(prog)s show Show current configuration
251
+ """,
252
+ )
253
+ parser.add_argument(
254
+ "--version",
255
+ action="version",
256
+ version="%(prog)s 0.2.2",
257
+ )
258
+
259
+ subparsers = parser.add_subparsers(dest="command", help="Commands")
260
+
261
+ # list command
262
+ list_parser = subparsers.add_parser("list", help="List available mirrors")
263
+ list_parser.add_argument(
264
+ "--json",
265
+ action="store_true",
266
+ help="Output as JSON",
267
+ )
268
+ list_parser.add_argument(
269
+ "--type",
270
+ type=str,
271
+ choices=["pip", "go", "npm"],
272
+ help="Filter by type",
273
+ )
274
+
275
+ # set command
276
+ set_parser = subparsers.add_parser("set", help="Configure mirror")
277
+ set_parser.add_argument(
278
+ "type",
279
+ type=str,
280
+ default="pip",
281
+ nargs="?",
282
+ choices=["pip", "go", "npm", "pnpm"],
283
+ help="Package manager type (default: pip)",
284
+ )
285
+ set_parser.add_argument(
286
+ "mirror",
287
+ type=str,
288
+ nargs="?",
289
+ default="pip",
290
+ help="Mirror to use",
291
+ )
292
+
293
+ # show command
294
+ show_parser = subparsers.add_parser("show", help="Show current mirror configuration")
295
+
296
+ args = parser.parse_args()
297
+
298
+ if args.command == "list":
299
+ mirrors = get_all_mirrors()
300
+ if getattr(args, "json", False):
301
+ print(json.dumps(mirrors, indent=2, ensure_ascii=False))
302
+ else:
303
+ print_mirrors()
304
+
305
+ elif args.command == "set":
306
+ mirror_type = args.type
307
+ mirror_key = args.mirror
308
+ success = configure_mirror(mirror_type, mirror_key)
309
+ sys.exit(0 if success else 1)
310
+
311
+ elif args.command == "show":
312
+ config = get_current_config()
313
+ if config:
314
+ print("\n📦 Current Mirror Configuration:")
315
+ print("-" * 40)
316
+ for key, value in config.items():
317
+ print(f" • {key}: {value}")
318
+ print()
319
+ else:
320
+ print("\n📦 No mirror configured")
321
+ print(" Run 'multi-lang-build mirror set <type> <mirror>' to configure")
322
+ print()
323
+ print_mirrors()
324
+
325
+ else:
326
+ # No subcommand, show help
327
+ parser.print_help()
328
+ print("\n📋 Commands:")
329
+ print(" list List all available mirrors")
330
+ print(" set <type> <name> Configure mirror (type: pip/go/npm)")
331
+ print(" show Show current configuration")
332
+ print("\n📋 Examples:")
333
+ print(" multi-lang-build mirror list")
334
+ print(" multi-lang-build mirror set pip")
335
+ print(" multi-lang-build mirror set go")
336
+ print(" multi-lang-build mirror set npm")
337
+
338
+
339
+ if __name__ == "__main__":
340
+ mirror_main()
@@ -25,36 +25,44 @@ def get_cli_path() -> str:
25
25
  return f"{sys.executable} -m multi_lang_build"
26
26
 
27
27
 
28
- def register_claude_code() -> bool:
28
+ def register_claude_code(project_level: bool = True) -> bool:
29
29
  """Register as Claude Code skill.
30
-
31
- Claude Code uses ~/.claude/CLAU.md for global instructions
32
- and project-specific skills via the API.
30
+
31
+ Args:
32
+ project_level: If True (default), register to project-level .claude/CLAUDE.md.
33
+ If False, register to global ~/.claude/CLAUDE.md.
33
34
  """
34
35
  try:
35
- # Check if claude CLI is available
36
- result = subprocess.run(
37
- ["which", "claude"],
38
- capture_output=True,
39
- text=True,
40
- )
41
-
42
- if result.returncode != 0:
43
- print("❌ Claude Code CLI not found. Please install it first:")
44
- print(" npm install -g @anthropic-ai/claude-code")
45
- return False
46
-
47
- # Create global instructions directory
48
- claude_dir = Path.home() / ".claude"
49
- claude_dir.mkdir(exist_ok=True)
50
-
51
- # Read existing CLAU.md or create new
52
- claude_md = claude_dir / "CLAUDE.md"
36
+ # Determine registration path
37
+ if project_level:
38
+ # Project-level: .claude/CLAUDE.md in current directory
39
+ config_dir = Path.cwd() / ".claude"
40
+ config_dir.mkdir(exist_ok=True)
41
+ claude_md = config_dir / "CLAUDE.md"
42
+ level_name = "project-level"
43
+ else:
44
+ # Global-level: ~/.claude/CLAUDE.md (only check CLI for global)
45
+ result = subprocess.run(
46
+ ["which", "claude"],
47
+ capture_output=True,
48
+ text=True,
49
+ )
50
+
51
+ if result.returncode != 0:
52
+ print("❌ Claude Code CLI not found. Please install it first:")
53
+ print(" npm install -g @anthropic-ai/claude-code")
54
+ return False
55
+
56
+ claude_dir = Path.home() / ".claude"
57
+ claude_dir.mkdir(exist_ok=True)
58
+ claude_md = claude_dir / "CLAUDE.md"
59
+ level_name = "global"
60
+
61
+ # Read existing content
53
62
  existing_content = ""
54
63
  if claude_md.exists():
55
64
  existing_content = claude_md.read_text()
56
-
57
- # Add multi-lang-build skill section
65
+
58
66
  skill_section = """
59
67
 
60
68
  ## Multi-Lang Build Tool
@@ -83,50 +91,51 @@ multi-lang-build pnpm ./src --output ./dist --mirror
83
91
 
84
92
  When working with Go projects, check if there are multiple main packages and use the `--target` flag to specify which one to build.
85
93
  """
86
-
94
+
87
95
  if "## Multi-Lang Build Tool" not in existing_content:
88
96
  with open(claude_md, "a") as f:
89
97
  f.write(skill_section)
90
- print(f"✅ Registered with Claude Code (added to {claude_md})")
98
+ print(f"✅ Registered with Claude Code ({level_name}, added to {claude_md})")
91
99
  else:
92
- print(f"ℹ️ Already registered with Claude Code ({claude_md})")
93
-
100
+ print(f"ℹ️ Already registered with Claude Code ({level_name}, {claude_md})")
101
+
94
102
  return True
95
-
103
+
96
104
  except Exception as e:
97
105
  print(f"❌ Failed to register with Claude Code: {e}")
98
106
  return False
99
107
 
100
108
 
101
- def register_opencode() -> bool:
109
+ def register_opencode(project_level: bool = True) -> bool:
102
110
  """Register as OpenCode skill.
103
-
104
- OpenCode typically uses ~/.config/opencode/ for configuration.
111
+
112
+ Args:
113
+ project_level: If True (default), register to project-level .opencode/skills.json.
114
+ If False, register to global ~/.config/opencode/skills.json.
105
115
  """
106
116
  try:
107
- # Check if opencode CLI is available
108
- result = subprocess.run(
109
- ["which", "opencode"],
110
- capture_output=True,
111
- text=True,
112
- )
113
-
114
- if result.returncode != 0:
115
- print("❌ OpenCode CLI not found. Please install it first.")
116
- return False
117
-
118
- # Create OpenCode config directory
119
- config_dir = Path.home() / ".config" / "opencode"
120
- config_dir.mkdir(parents=True, exist_ok=True)
121
-
117
+ # Determine registration path
118
+ if project_level:
119
+ # Project-level: .opencode/skills.json in current directory
120
+ base_dir = Path.cwd()
121
+ config_dir = base_dir / ".opencode"
122
+ config_dir.mkdir(exist_ok=True)
123
+ skills_file = config_dir / "skills.json"
124
+ level_name = "project-level"
125
+ else:
126
+ # Global-level: ~/.config/opencode/skills.json
127
+ config_dir = Path.home() / ".config" / "opencode"
128
+ config_dir.mkdir(parents=True, exist_ok=True)
129
+ skills_file = config_dir / "skills.json"
130
+ level_name = "global"
131
+
122
132
  # Create or update skills.json
123
- skills_file = config_dir / "skills.json"
124
133
  skills: dict = {}
125
-
134
+
126
135
  if skills_file.exists():
127
136
  with open(skills_file) as f:
128
137
  skills = json.load(f)
129
-
138
+
130
139
  # Add multi-lang-build skill
131
140
  skills["multi-lang-build"] = {
132
141
  "name": "multi-lang-build",
@@ -143,47 +152,48 @@ def register_opencode() -> bool:
143
152
  "multi-lang-build pnpm ./src --output ./dist --mirror",
144
153
  ]
145
154
  }
146
-
155
+
147
156
  with open(skills_file, "w") as f:
148
157
  json.dump(skills, f, indent=2)
149
-
150
- print(f"✅ Registered with OpenCode (config: {skills_file})")
158
+
159
+ print(f"✅ Registered with OpenCode ({level_name}, config: {skills_file})")
151
160
  return True
152
-
161
+
153
162
  except Exception as e:
154
163
  print(f"❌ Failed to register with OpenCode: {e}")
155
164
  return False
156
165
 
157
166
 
158
- def register_trae() -> bool:
167
+ def register_trae(project_level: bool = True) -> bool:
159
168
  """Register as Trae skill.
160
-
161
- Trae typically uses ~/.trae/ for configuration.
169
+
170
+ Args:
171
+ project_level: If True (default), register to project-level .trae/skills.json.
172
+ If False, register to global ~/.trae/skills.json.
162
173
  """
163
174
  try:
164
- # Check if trae CLI is available
165
- result = subprocess.run(
166
- ["which", "trae"],
167
- capture_output=True,
168
- text=True,
169
- )
170
-
171
- if result.returncode != 0:
172
- print("❌ Trae CLI not found. Please install it first.")
173
- return False
174
-
175
- # Create Trae config directory
176
- config_dir = Path.home() / ".trae"
177
- config_dir.mkdir(exist_ok=True)
178
-
175
+ # Determine registration path
176
+ if project_level:
177
+ # Project-level: .trae/skills.json in current directory
178
+ base_dir = Path.cwd()
179
+ config_dir = base_dir / ".trae"
180
+ config_dir.mkdir(exist_ok=True)
181
+ skills_file = config_dir / "skills.json"
182
+ level_name = "project-level"
183
+ else:
184
+ # Global-level: ~/.trae/skills.json
185
+ config_dir = Path.home() / ".trae"
186
+ config_dir.mkdir(exist_ok=True)
187
+ skills_file = config_dir / "skills.json"
188
+ level_name = "global"
189
+
179
190
  # Create or update skills.json
180
- skills_file = config_dir / "skills.json"
181
191
  skills: dict = {}
182
-
192
+
183
193
  if skills_file.exists():
184
194
  with open(skills_file) as f:
185
195
  skills = json.load(f)
186
-
196
+
187
197
  # Add multi-lang-build skill
188
198
  skills["multi-lang-build"] = {
189
199
  "name": "Multi-Lang Build",
@@ -210,49 +220,48 @@ def register_trae() -> bool:
210
220
  }
211
221
  ]
212
222
  }
213
-
223
+
214
224
  with open(skills_file, "w") as f:
215
225
  json.dump(skills, f, indent=2)
216
-
217
- print(f"✅ Registered with Trae (config: {skills_file})")
226
+
227
+ print(f"✅ Registered with Trae ({level_name}, config: {skills_file})")
218
228
  return True
219
-
229
+
220
230
  except Exception as e:
221
231
  print(f"❌ Failed to register with Trae: {e}")
222
232
  return False
223
233
 
224
234
 
225
- def register_codebuddy() -> bool:
235
+ def register_codebuddy(project_level: bool = True) -> bool:
226
236
  """Register as CodeBuddy skill.
227
-
228
- CodeBuddy typically uses ~/.codebuddy/ for configuration.
237
+
238
+ Args:
239
+ project_level: If True (default), register to project-level .codebuddy/skills.yaml.
240
+ If False, register to global ~/.codebuddy/skills.yaml.
229
241
  """
230
242
  try:
231
- # Check if codebuddy CLI is available
232
- result = subprocess.run(
233
- ["which", "codebuddy"],
234
- capture_output=True,
235
- text=True,
236
- )
237
-
238
- if result.returncode != 0:
239
- print("❌ CodeBuddy CLI not found. Please install it first.")
240
- return False
241
-
242
- # Create CodeBuddy config directory
243
- config_dir = Path.home() / ".codebuddy"
244
- config_dir.mkdir(exist_ok=True)
245
-
246
- # Create or update skills.yaml (CodeBuddy may use YAML)
247
- skills_file = config_dir / "skills.yaml"
248
-
243
+ # Determine registration path
244
+ if project_level:
245
+ # Project-level: .codebuddy/skills.yaml in current directory
246
+ base_dir = Path.cwd()
247
+ config_dir = base_dir / ".codebuddy"
248
+ config_dir.mkdir(exist_ok=True)
249
+ skills_file = config_dir / "skills.yaml"
250
+ level_name = "project-level"
251
+ else:
252
+ # Global-level: ~/.codebuddy/skills.yaml
253
+ config_dir = Path.home() / ".codebuddy"
254
+ config_dir.mkdir(exist_ok=True)
255
+ skills_file = config_dir / "skills.yaml"
256
+ level_name = "global"
257
+
249
258
  skill_config = """# Multi-Lang Build Skill
250
259
  skills:
251
260
  multi-lang-build:
252
261
  name: "Multi-Lang Build"
253
262
  description: "Multi-language automated build tool with mirror acceleration"
254
263
  version: "0.2.0"
255
-
264
+
256
265
  commands:
257
266
  build-go:
258
267
  description: "Build Go project"
@@ -270,7 +279,7 @@ skills:
270
279
  - name: mirror
271
280
  description: "Enable mirror acceleration"
272
281
  type: flag
273
-
282
+
274
283
  build-python:
275
284
  description: "Build Python project"
276
285
  command: "multi-lang-build python {source} --output {output}"
@@ -284,7 +293,7 @@ skills:
284
293
  - name: mirror
285
294
  description: "Enable mirror acceleration"
286
295
  type: flag
287
-
296
+
288
297
  build-pnpm:
289
298
  description: "Build pnpm project"
290
299
  command: "multi-lang-build pnpm {source} --output {output}"
@@ -299,65 +308,67 @@ skills:
299
308
  description: "Enable mirror acceleration"
300
309
  type: flag
301
310
  """
302
-
311
+
303
312
  # Append or create
304
313
  if skills_file.exists():
305
314
  content = skills_file.read_text()
306
315
  if "multi-lang-build" not in content:
307
316
  with open(skills_file, "a") as f:
308
317
  f.write("\n" + skill_config)
309
- print(f"✅ Registered with CodeBuddy (updated {skills_file})")
318
+ print(f"✅ Registered with CodeBuddy ({level_name}, updated {skills_file})")
310
319
  else:
311
- print(f"ℹ️ Already registered with CodeBuddy ({skills_file})")
320
+ print(f"ℹ️ Already registered with CodeBuddy ({level_name}, {skills_file})")
312
321
  else:
313
322
  skills_file.write_text(skill_config)
314
- print(f"✅ Registered with CodeBuddy (created {skills_file})")
315
-
323
+ print(f"✅ Registered with CodeBuddy ({level_name}, created {skills_file})")
324
+
316
325
  return True
317
-
326
+
318
327
  except Exception as e:
319
328
  print(f"❌ Failed to register with CodeBuddy: {e}")
320
329
  return False
321
330
 
322
331
 
323
332
  def register_skill(
324
- ide: Literal["claude", "opencode", "trae", "codebuddy", "all"] = "claude"
333
+ ide: Literal["claude", "opencode", "trae", "codebuddy", "all"] = "claude",
334
+ global_level: bool = False
325
335
  ) -> bool:
326
336
  """Register multi-lang-build as a skill for the specified IDE.
327
-
337
+
328
338
  Args:
329
339
  ide: The IDE to register with. Options: "claude", "opencode", "trae", "codebuddy", "all"
330
340
  Default is "claude".
331
-
341
+ global_level: If True, register to global config. If False (default), register to project-level.
342
+
332
343
  Returns:
333
344
  True if registration succeeded, False otherwise.
334
345
  """
335
346
  print(f"📝 Registering multi-lang-build for {ide}...\n")
336
-
347
+
337
348
  if ide == "all":
338
349
  results = [
339
- ("Claude Code", register_claude_code()),
340
- ("OpenCode", register_opencode()),
341
- ("Trae", register_trae()),
342
- ("CodeBuddy", register_codebuddy()),
350
+ ("Claude Code", register_claude_code(project_level=not global_level)),
351
+ ("OpenCode", register_opencode(project_level=not global_level)),
352
+ ("Trae", register_trae(project_level=not global_level)),
353
+ ("CodeBuddy", register_codebuddy(project_level=not global_level)),
343
354
  ]
344
-
355
+
345
356
  print("\n" + "=" * 50)
346
357
  print("Registration Summary:")
347
358
  for name, success in results:
348
359
  status = "✅" if success else "❌"
349
360
  print(f" {status} {name}")
350
-
361
+
351
362
  return all(success for _, success in results)
352
-
363
+
353
364
  elif ide == "claude":
354
- return register_claude_code()
365
+ return register_claude_code(project_level=not global_level)
355
366
  elif ide == "opencode":
356
- return register_opencode()
367
+ return register_opencode(project_level=not global_level)
357
368
  elif ide == "trae":
358
- return register_trae()
369
+ return register_trae(project_level=not global_level)
359
370
  elif ide == "codebuddy":
360
- return register_codebuddy()
371
+ return register_codebuddy(project_level=not global_level)
361
372
  else:
362
373
  print(f"❌ Unknown IDE: {ide}")
363
374
  print("Supported IDEs: claude, opencode, trae, codebuddy, all")
@@ -377,7 +388,13 @@ if __name__ == "__main__":
377
388
  choices=["claude", "opencode", "trae", "codebuddy", "all"],
378
389
  help="IDE to register with (default: claude)",
379
390
  )
380
-
391
+ parser.add_argument(
392
+ "--global",
393
+ dest="global_level",
394
+ action="store_true",
395
+ help="Register to global config instead of project-level",
396
+ )
397
+
381
398
  args = parser.parse_args()
382
- success = register_skill(args.ide)
399
+ success = register_skill(args.ide, global_level=args.global_level)
383
400
  sys.exit(0 if success else 1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multi-lang-build
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Multi-language automated build tool with domestic mirror acceleration support
5
5
  Project-URL: Homepage, https://github.com/example/multi-lang-build
6
6
  Project-URL: Repository, https://github.com/example/multi-lang-build
@@ -1,7 +1,7 @@
1
- multi_lang_build/__init__.py,sha256=FvlY43Kj6VO5iwosAJy3GU9_QdAWmdF_bd3_G8rOg78,1626
2
- multi_lang_build/cli.py,sha256=MV_RnKcHBCy6v6XQdkQCTFOy5gTuHlNCtZ1K1k8OB2M,10134
1
+ multi_lang_build/__init__.py,sha256=ry3OjqGqFAX6uOwmByd_c3CyxADdD4Sq1QXTqh-ECqE,1777
2
+ multi_lang_build/cli.py,sha256=GpzokqRKERuVUKpB7ueDm1zCbF9R8lKid2IClU7rG6o,12327
3
3
  multi_lang_build/py.typed,sha256=c8jtFarMovqA5DdwhEzKPH4WrX85xaoiWilIlvuc6KU,84
4
- multi_lang_build/register.py,sha256=0-dPFAfY0fTydAQXvukaauNOno4rRty0bO_2yYENKH4,12349
4
+ multi_lang_build/register.py,sha256=aws1xEaNKH4pMkwBxoKizPh8Anr1Wz0ukUhi_d5In_w,13840
5
5
  multi_lang_build/types.py,sha256=iCyz_6KmEBU4xwpCtfJnpzXoiSsmOcOKOhzHJVx8Z4Q,1043
6
6
  multi_lang_build/compiler/__init__.py,sha256=y5vvVN6HdSzq4W1kXBMy_vQq9G3-TtiVcNMOyBQHbCw,484
7
7
  multi_lang_build/compiler/base.py,sha256=jZvXh16KA0-kxd6ueY2xNtMoFNN9g8Kho3-2Ys4RQ7w,5743
@@ -9,9 +9,10 @@ multi_lang_build/compiler/go.py,sha256=pwrZe4FOUEjl1EASGrW1JQ1y26l-06ArW7H42l57B
9
9
  multi_lang_build/compiler/pnpm.py,sha256=7JiN4SoPy6oUkmBp_4RLuGLbq_3J5no_vsd6rGdPMBY,14014
10
10
  multi_lang_build/compiler/python.py,sha256=Vsw9JFuZQPkSwFbNbirUpb4613ti3kaq3EVvYPVCXrc,17320
11
11
  multi_lang_build/mirror/__init__.py,sha256=RrZOQ83bxImAR275zeAUKyyCgwqdGt5xH8tF_-UShJo,489
12
+ multi_lang_build/mirror/cli.py,sha256=qv6RI6izxUIsfMCIDOtTK3avcNSJpAaUF3NPiK1W49A,9820
12
13
  multi_lang_build/mirror/config.py,sha256=d9KJoV80BUZOR9R3DpcT1r0nyxH1HK6hfLZU7IsUGcw,8552
13
- multi_lang_build-0.2.0.dist-info/METADATA,sha256=8unid7gcxTG3YY0oJiblVuI3EMKNn6KHpYxisHtQSck,9055
14
- multi_lang_build-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
15
- multi_lang_build-0.2.0.dist-info/entry_points.txt,sha256=1JxmXQltgTEvzWWu2u4aKRX_z8DoDM3VYJfCncjBaYY,259
16
- multi_lang_build-0.2.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
17
- multi_lang_build-0.2.0.dist-info/RECORD,,
14
+ multi_lang_build-0.2.2.dist-info/METADATA,sha256=lNl81Mh9q9y08-8r50fVuBdC_JVmKPSUWy2GRdkMBK4,9055
15
+ multi_lang_build-0.2.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
16
+ multi_lang_build-0.2.2.dist-info/entry_points.txt,sha256=0vd5maQH5aZoqnpbr2Yr_IWB_-ncWX1rO3jGBCW3pHY,319
17
+ multi_lang_build-0.2.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
18
+ multi_lang_build-0.2.2.dist-info/RECORD,,
@@ -1,6 +1,7 @@
1
1
  [console_scripts]
2
2
  go-build = multi_lang_build.compiler.go:main
3
3
  multi-lang-build = multi_lang_build:main
4
+ multi-lang-mirror = multi_lang_build.mirror.cli:mirror_main
4
5
  multi-lang-register = multi_lang_build:main_register
5
6
  pnpm-build = multi_lang_build.compiler.pnpm:main
6
7
  python-build = multi_lang_build.compiler.python:main