refactor(skill): apply ce-code-review fixes — bump to 3.2.2, fallback tests, comparison resolver
12 fixes from the multi-agent code review on PR #400: Version 3.2.1 -> 3.2.2 across all manifests (SKILL.md frontmatter + body header, pyproject.toml, .claude-plugin/{plugin,marketplace}.json, sync.sh cache path). The PR ships observable behavior changes (STEP 0 logic flip, resolver order change, badge fallback) that should not silently appear under the same version number — the new fallback reads SKILL.md version directly so the badge would otherwise be misleading. render.py::_skill_version: - `import re` moved to module top - _VERSION_RE extracted as a module-level compiled pattern that accepts double-quoted, single-quoted, and unquoted YAML version scalars - `break` -> `continue` on corrupt manifest, so a corrupt inner manifest no longer shadows a valid outer one - Wrap SKILL.md read_text() in try/except for UnicodeDecodeError to keep badge emission from crashing on mis-encoded SKILL.md - Docstring clarifies precedence; inline comment marks the fallback boundary between the manifest walk and the SKILL.md walk tests/test_skill_version.py (new): 7 unit tests for the fallback paths (manifest absent, manifest corrupt, corrupt-inner + valid-outer, both absent, SKILL.md without version, single-quoted, unquoted). tests/test_plugin_contract.py: tombstone test asserting .codex-plugin/ stays removed (was the only CI guard against accidental reintroduction). SKILL.md: - STEP 0 bash echoes CLAUDE_CACHE_LATEST so the model can see the resolved value when deciding whether to hop - "Both shapes ship in the wild" comment now names the two cache layouts (nested {cache}/{version}/skills/last30days/ vs flat {cache}/{version}/) - Comparison-mode bash invocation gets its own inline SKILL_ROOT resolver (latent gap: the contract tells the model to skip Step 1 on comparison queries, so SKILL_ROOT was previously unset there) CHANGELOG.md: [Unreleased] entries for the resolver rewrite and the breaking removal of Codex native-plugin support. All 9 reviewer personas surfaced findings; 3 cross-reviewer corroboration clusters were promoted (import re, "both shapes" comment, missing fallback tests). Maintainability follow-up flagged: regex now duplicated across render.py and 2 test files; could consolidate via shared lib/skill_meta.py helper in a future PR.
This commit is contained in:
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
import pathlib
|
||||
import re
|
||||
from collections import Counter
|
||||
from datetime import date
|
||||
from urllib.parse import urlparse
|
||||
@@ -11,15 +12,23 @@ from urllib.parse import urlparse
|
||||
from . import dates, schema
|
||||
|
||||
|
||||
_VERSION_RE = re.compile(
|
||||
r'''^version:\s*(?:"([^"]+)"|'([^']+)'|(\S+))\s*$''',
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
|
||||
def _skill_version() -> str:
|
||||
"""Read plugin version from .claude-plugin/plugin.json, falling back to SKILL.md frontmatter.
|
||||
|
||||
sync.sh does not copy .claude-plugin/ to non-cache install dirs (~/.codex/skills,
|
||||
~/.agents/skills, Hermes), so SKILL.md frontmatter is the fallback that keeps the
|
||||
badge from emitting v? on those installs. Returns "?" only if both sources are missing.
|
||||
"""
|
||||
import re
|
||||
|
||||
A corrupt manifest at one ancestor does not shadow a valid manifest at a deeper one
|
||||
(continue, not break). YAML frontmatter accepts double-quoted, single-quoted, or
|
||||
unquoted version scalars.
|
||||
"""
|
||||
here = pathlib.Path(__file__).resolve()
|
||||
for parent in here.parents:
|
||||
manifest = parent / ".claude-plugin" / "plugin.json"
|
||||
@@ -27,14 +36,18 @@ def _skill_version() -> str:
|
||||
try:
|
||||
return json.loads(manifest.read_text()).get("version", "?")
|
||||
except (json.JSONDecodeError, OSError):
|
||||
break
|
||||
continue
|
||||
|
||||
# No manifest found at any ancestor — fall back to SKILL.md frontmatter.
|
||||
for parent in here.parents:
|
||||
skill_md = parent / "SKILL.md"
|
||||
if skill_md.is_file():
|
||||
match = re.search(r'^version:\s*"([^"]+)"\s*$', skill_md.read_text(), re.MULTILINE)
|
||||
try:
|
||||
match = _VERSION_RE.search(skill_md.read_text())
|
||||
except (OSError, UnicodeDecodeError):
|
||||
break
|
||||
if match:
|
||||
return match.group(1)
|
||||
return next(g for g in match.groups() if g is not None)
|
||||
break
|
||||
return "?"
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ COMMON_TARGETS=(
|
||||
# against the working tree so /last30days reflects local dev without
|
||||
# waiting for a release. Do NOT add ~/.claude/skills/last30days - it
|
||||
# creates a duplicate slash-command entry alongside the plugin version.
|
||||
"$HOME/.claude/plugins/cache/last30days-skill/last30days/3.2.1"
|
||||
"$HOME/.claude/plugins/cache/last30days-skill/last30days/3.2.2"
|
||||
"$HOME/.agents/skills/last30days"
|
||||
"$HOME/.codex/skills/last30days"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user