* fix(skills): preserve non-ASCII chars in skill frontmatter
Skill SKILL.md frontmatter descriptions containing non-ASCII
characters were escaped to \uXXXX / \xXX sequences because
yaml.safe_dump() was called without allow_unicode=True.
- Add allow_unicode=True to the 7 skill/command frontmatter
safe_dump sites (extensions, presets, claude integration)
- Add regression tests for the render and extension-install paths
Follows the approach of #1936; encoding="utf-8" is already set on
the affected write paths, so no encoding change is needed here.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* refactor(_utils): add dump_frontmatter helper
Centralize skill/command frontmatter YAML serialization into a single
_utils.dump_frontmatter helper so no call site can drop allow_unicode or
diverge on formatting. Route the 7 existing sites through it and drop a
now-unused local yaml import.
Switch the extension test fixtures to yaml.safe_dump for parity with the
production safe-dump/safe-load codepaths.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* refactor(presets): convert presets.py module to presets/ package
Pure structural move to mirror integrations/. presets.py becomes
presets/__init__.py with relative imports rebased one level deeper.
No behavior change; public import surface (from .presets import ...)
preserved. Prepares for co-locating preset command handlers in PR-6/8.
* refactor: move preset command handlers to presets/_commands.py (PR-6/8)
Cut the preset_app / preset_catalog_app Typer groups and all 12 command
handlers out of __init__.py into presets/_commands.py, exposing register(app)
— mirrors the integration co-location from PR-5. __init__.py now registers
via _register_preset_cmds(app), dropping ~620 lines (3282 -> 2663).
Handlers lazy-import root helpers (_require_specify_project, get_speckit_version,
_locate_bundled_preset, _display_project_path) via 'from .. import' so test
monkeypatching of specify_cli.<helper> keeps working. _locate_bundled_preset
kept as an explicit re-export in __init__.py for that resolution path.
CLI surface and public imports unchanged. Full suite: 3162 passed, 40 skipped.