mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-06-10 16:14:54 +03:00
feat(switch): emit -ExecutionPolicy Bypass for codex target
Codex on Windows launches powershell.exe as a login-shell that loads the user profile despite -NoProfile in our SKILL.md. With Restricted ExecutionPolicy this spams "выполнение сценариев отключено". Add -ExecutionPolicy Bypass for codex; keep canonical -NoProfile -File for all other platforms. Round-trip safe: cmd_install always copies fresh from .claude/skills/, so switching codex→cursor strips the EP flag. cmd_switch_runtime re-emits PS commands via normalize_ps_invocation each pass, so in-place py↔ps in .codex/skills/ keeps the flag. Also fix a pre-existing bug in cmd_switch_runtime: file-existence check used repo_root() instead of project_dir, so in-place runtime switch in a foreign project always tripped skip_runtime=True and became a no-op. The bug was masked when project_dir == repo_root (source-repo workflow). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+46
-11
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# switch.py v1.5 — Переключение навыков 1С между AI-платформами и рантаймами
|
# switch.py v1.6 — Переключение навыков 1С между AI-платформами и рантаймами
|
||||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||||
"""
|
"""
|
||||||
Копирует (или создаёт ссылки на) навыки из .claude/skills/ на другие AI-платформы
|
Копирует (или создаёт ссылки на) навыки из .claude/skills/ на другие AI-платформы
|
||||||
@@ -63,9 +63,35 @@ GITIGNORE_RECOMMENDATIONS = [
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Capture optional surrounding quote (group 'q') and bare path (group 'path').
|
# Capture optional surrounding quote (group 'q') and bare path (group 'path').
|
||||||
# Path matches non-whitespace non-quote chars to support ${CLAUDE_SKILL_DIR}/...
|
# Path matches non-whitespace non-quote chars to support ${CLAUDE_SKILL_DIR}/...
|
||||||
RX_PS = re.compile(r'powershell\.exe\s+(?:-NoProfile\s+)?-File\s+(?P<q>["\']?)(?P<path>[^"\s]+?)\.ps1(?P=q)?')
|
# Optional -ExecutionPolicy <value> between -NoProfile and -File (used for codex target).
|
||||||
|
RX_PS = re.compile(
|
||||||
|
r'powershell\.exe\s+'
|
||||||
|
r'(?:-NoProfile\s+)?'
|
||||||
|
r'(?:-ExecutionPolicy\s+\S+\s+)?'
|
||||||
|
r'-File\s+(?P<q>["\']?)(?P<path>[^"\s]+?)\.ps1(?P=q)?'
|
||||||
|
)
|
||||||
RX_PY = re.compile(r"python\s+(?P<q>[\"']?)(?P<path>[^\"\s]+?)\.py(?P=q)?")
|
RX_PY = re.compile(r"python\s+(?P<q>[\"']?)(?P<path>[^\"\s]+?)\.py(?P=q)?")
|
||||||
|
|
||||||
|
# Платформы, требующие -ExecutionPolicy Bypass (Codex запускает powershell как
|
||||||
|
# login-shell, профиль грузится и упирается в Restricted policy).
|
||||||
|
PS_BYPASS_PLATFORMS = {'codex'}
|
||||||
|
|
||||||
|
|
||||||
|
def emit_ps_invocation(path, quote, platform):
|
||||||
|
"""Build canonical powershell.exe invocation for a target platform."""
|
||||||
|
ep = ' -ExecutionPolicy Bypass' if platform in PS_BYPASS_PLATFORMS else ''
|
||||||
|
return f"powershell.exe -NoProfile{ep} -File {quote}{path}.ps1{quote}"
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_ps_invocation(content, platform):
|
||||||
|
"""Re-emit existing powershell.exe ...ps1 invocations with platform flags.
|
||||||
|
|
||||||
|
Idempotent: matches both with and without -ExecutionPolicy in source.
|
||||||
|
"""
|
||||||
|
def repl(m):
|
||||||
|
return emit_ps_invocation(m.group('path'), m.group('q'), platform)
|
||||||
|
return RX_PS.sub(repl, content)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Junction / symlink helpers
|
# Junction / symlink helpers
|
||||||
@@ -221,8 +247,12 @@ def rewrite_paths(content, platform, target_prefix, skill_name):
|
|||||||
return content
|
return content
|
||||||
|
|
||||||
|
|
||||||
def switch_runtime_content(content, target_runtime):
|
def switch_runtime_content(content, target_runtime, platform='claude-code'):
|
||||||
"""Switch runtime invocations in .md content. Returns (new_content, switched)."""
|
"""Switch runtime invocations in .md content. Returns (new_content, switched).
|
||||||
|
|
||||||
|
Platform controls flags on emitted powershell invocations
|
||||||
|
(codex requires -ExecutionPolicy Bypass).
|
||||||
|
"""
|
||||||
if target_runtime == 'python':
|
if target_runtime == 'python':
|
||||||
def to_py(m):
|
def to_py(m):
|
||||||
q = m.group('q')
|
q = m.group('q')
|
||||||
@@ -230,8 +260,7 @@ def switch_runtime_content(content, target_runtime):
|
|||||||
new = RX_PS.sub(to_py, content)
|
new = RX_PS.sub(to_py, content)
|
||||||
elif target_runtime == 'powershell':
|
elif target_runtime == 'powershell':
|
||||||
def to_ps(m):
|
def to_ps(m):
|
||||||
q = m.group('q')
|
return emit_ps_invocation(m.group('path'), m.group('q'), platform)
|
||||||
return f"powershell.exe -NoProfile -File {q}{m.group('path')}.ps1{q}"
|
|
||||||
new = RX_PY.sub(to_ps, content)
|
new = RX_PY.sub(to_ps, content)
|
||||||
else:
|
else:
|
||||||
return content, False
|
return content, False
|
||||||
@@ -354,9 +383,13 @@ def cmd_install(platform, runtime, project_dir):
|
|||||||
# where target runtime is not available)
|
# where target runtime is not available)
|
||||||
if not skip_runtime:
|
if not skip_runtime:
|
||||||
if runtime == 'python':
|
if runtime == 'python':
|
||||||
new_content, _ = switch_runtime_content(new_content, 'python')
|
new_content, _ = switch_runtime_content(new_content, 'python', platform)
|
||||||
elif runtime == 'powershell':
|
elif runtime == 'powershell':
|
||||||
new_content, _ = switch_runtime_content(new_content, 'powershell')
|
new_content, _ = switch_runtime_content(new_content, 'powershell', platform)
|
||||||
|
|
||||||
|
# Normalize any remaining powershell invocations with platform flags
|
||||||
|
# (covers skip_runtime=True case where source PS commands stayed)
|
||||||
|
new_content = normalize_ps_invocation(new_content, platform)
|
||||||
|
|
||||||
if new_content != content:
|
if new_content != content:
|
||||||
with open(md_path, 'w', encoding='utf-8') as f:
|
with open(md_path, 'w', encoding='utf-8') as f:
|
||||||
@@ -491,14 +524,14 @@ def cmd_switch_runtime(runtime, project_dir):
|
|||||||
# Skip runtime conversion for single-runtime skills where
|
# Skip runtime conversion for single-runtime skills where
|
||||||
# target files don't exist (e.g. img-grid has only .py)
|
# target files don't exist (e.g. img-grid has only .py)
|
||||||
cur_rt = classify_skill_runtime(skill_path)
|
cur_rt = classify_skill_runtime(skill_path)
|
||||||
missing = check_missing_files(skill_path, runtime, repo_root())
|
missing = check_missing_files(skill_path, runtime, project_dir)
|
||||||
skip_runtime = bool(missing) and (
|
skip_runtime = bool(missing) and (
|
||||||
(runtime == 'python' and cur_rt in ('ps', 'none'))
|
(runtime == 'python' and cur_rt in ('ps', 'none'))
|
||||||
or (runtime == 'powershell' and cur_rt in ('py', 'none'))
|
or (runtime == 'powershell' and cur_rt in ('py', 'none'))
|
||||||
)
|
)
|
||||||
|
|
||||||
info, warnings = collect_runtime_messages(
|
info, warnings = collect_runtime_messages(
|
||||||
skill_name, skill_path, runtime, repo_root())
|
skill_name, skill_path, runtime, project_dir)
|
||||||
all_info.extend(info)
|
all_info.extend(info)
|
||||||
all_warnings.extend(warnings)
|
all_warnings.extend(warnings)
|
||||||
|
|
||||||
@@ -509,7 +542,9 @@ def cmd_switch_runtime(runtime, project_dir):
|
|||||||
with open(md_path, 'r', encoding='utf-8') as f:
|
with open(md_path, 'r', encoding='utf-8') as f:
|
||||||
content = f.read()
|
content = f.read()
|
||||||
|
|
||||||
new_content, changed = switch_runtime_content(content, runtime)
|
new_content, _ = switch_runtime_content(content, runtime, platform_name)
|
||||||
|
new_content = normalize_ps_invocation(new_content, platform_name)
|
||||||
|
changed = new_content != content
|
||||||
|
|
||||||
if changed:
|
if changed:
|
||||||
with open(md_path, 'w', encoding='utf-8') as f:
|
with open(md_path, 'w', encoding='utf-8') as f:
|
||||||
|
|||||||
Reference in New Issue
Block a user