Skip to content

0.2.0 wheel: missing prompt .md data files + Windows temp-dir cleanup crash (claude backend) #117

Description

@ray8875

Thanks for open-sourcing SkillOpt! While setting up 0.2.0 on Windows with the Claude backend I hit two blocking bugs. Bug 1 affects all platforms; Bug 2 is Windows-specific. A third, minor Windows note is at the end.

Environment

  • skillopt 0.2.0, installed from PyPI: pip install "skillopt[claude]"
  • Python 3.14.5, Windows 10
  • Backend: --backend claude (claude-agent-sdk; Claude Code CLI 2.1.173)

Bug 1 — PyPI wheel ships no prompt files (skillopt/prompts/*.md missing) — all platforms

The wheel installs skillopt/prompts/ containing only __init__.py; every generic prompt .md is missing, as are the per-env prompts under skillopt/envs/<env>/prompts/. Any run fails as soon as it needs a prompt:

FileNotFoundError: Prompt 'analyst_error' not found. Searched: skillopt/prompts/analyst_error.md

(The rollout stage fails the same way on rollout_system.)

Repro:

pip install skillopt
python -c "import os, skillopt; p=os.path.join(os.path.dirname(skillopt.__file__),'prompts'); print(sorted(os.listdir(p)))"
# -> ['__init__.py', '__pycache__']   # all *.md missing

Root cause: the .md files aren't declared as package data, so they're excluded from the wheel; load_prompt() (skillopt/prompts/__init__.py) then can't resolve them. The repo tree has all ~21 generic prompts + per-env prompts, so a source / editable install works — only the wheel is broken.

Suggested fix (setuptools / pyproject):

[tool.setuptools.package-data]
"*" = ["*.md"]

or an equivalent MANIFEST.in (recursive-include skillopt *.md) with include_package_data. A test that imports each registered env and asserts load_prompt(...) resolves would catch regressions.


Bug 2 — Windows: temp-dir cleanup race in the Claude backend → WinError 32

skillopt/model/claude_backend.py::_run_claude_print runs the CLI inside a TemporaryDirectory and passes it as the child's cwd:

with tempfile.TemporaryDirectory(prefix="skillopt_claude_") as temp_dir:   # ~L246
    ...
    proc = subprocess.run(cmd, ..., cwd=temp_dir)                          # ~L273

On Windows the spawned claude / node process still holds a handle on temp_dir when the with block exits, so TemporaryDirectory cleanup raises and the exception propagates out of the model call (masking the real result):

RuntimeError: Claude backend failed after N retries:
[WinError 32] The process cannot access the file because it is being used by another process:
'...\Temp\skillopt_claude_xxxxxxxx'

This makes the claude backend unusable on Windows. Since requires-python >= 3.10, ignore_cleanup_errors=True fixes it cleanly:

with tempfile.TemporaryDirectory(prefix="skillopt_claude_", ignore_cleanup_errors=True) as temp_dir:

(Alternatives: don't set the child cwd to the temp dir, or retry the cleanup.)


Also on Windows (minor) — Unicode print crashes under cp1252

The trainer prints non-ASCII (e.g. the in the [gate] line, skillopt/engine/trainer.py), which crashes under the default Windows code page before any model call:

UnicodeEncodeError: 'charmap' codec can't encode character '→' ... cp1252

PYTHONUTF8=1 works around it; reconfiguring stdout to UTF-8 (or using ASCII in logs) would fix it for everyone.


With all three addressed, an end-to-end skillopt-train --backend claude run completes cleanly (EXIT=0). Happy to send a PR for Bugs 1 & 2 if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions