mirror of
https://github.com/conorbronsdon/avoid-ai-writing.git
synced 2026-09-19 01:32:11 +08:00
049463a806
* feat: distribute as a Claude Code / Cowork plugin Package the skill as a single-plugin marketplace so it can be installed via /plugin instead of a bare clone. This is the only path that works in Claude Cowork, which loads skills exclusively from installed plugins (see #12). Root SKILL.md stays the single source of truth; the plugin's bundled copy at plugins/avoid-ai-writing/skills/avoid-ai-writing/SKILL.md is generated by scripts/sync-plugin-skill.sh. A CI workflow regenerates it and fails on drift or a plugin.json/SKILL.md version mismatch. Validated with `claude plugin validate` and a full local add-marketplace / install / inspect / uninstall cycle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix: parse plugin version safely in sync script * fix: harden plugin version parsing in sync script * harden skill-version parsing + plugin install docs Builds on Copilot's plugin.json JSON-parser fix: - sync-plugin-skill.sh: read the version only from the SKILL.md frontmatter block (not anywhere in the file), strip CR, and fail with a clear message if it's unparseable — closes the loose/false-green side of the version check that the JSON parser already closed for plugin.json. - .gitattributes: pin *.md to LF so a CRLF checkout can't forge a version mismatch or mask skill drift. - README: add /reload-plugins to the install steps (skills don't activate until reload/restart) and note updates are version-gated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
53 lines
1.8 KiB
Bash
Executable File
53 lines
1.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Regenerate the plugin's bundled skill from the canonical root SKILL.md.
|
|
# Root SKILL.md is the single source of truth; the plugin copy is generated.
|
|
# Run this after editing SKILL.md. CI fails if the copy is out of sync.
|
|
set -euo pipefail
|
|
|
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
src="$repo_root/SKILL.md"
|
|
dest="$repo_root/plugins/avoid-ai-writing/skills/avoid-ai-writing/SKILL.md"
|
|
|
|
cp "$src" "$dest"
|
|
|
|
# Keep plugin.json's version in lockstep with the SKILL.md frontmatter version.
|
|
# Read the version only from the first YAML frontmatter block, and strip any CR
|
|
# so a CRLF checkout can't forge a mismatch on visually-identical strings.
|
|
skill_version="$(sed -n '/^---[[:space:]]*$/,/^---[[:space:]]*$/ s/^version:[[:space:]]*//p' "$src" | head -n1 | tr -d '\r')"
|
|
if [ -z "$skill_version" ]; then
|
|
echo "could not parse 'version:' from SKILL.md frontmatter" >&2
|
|
exit 1
|
|
fi
|
|
plugin_version="$(
|
|
python3 - "$repo_root/plugins/avoid-ai-writing/.claude-plugin/plugin.json" <<'PY'
|
|
import json
|
|
import sys
|
|
|
|
path = sys.argv[1]
|
|
try:
|
|
with open(path, encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
except FileNotFoundError:
|
|
print(f"Missing plugin manifest: {path}", file=sys.stderr)
|
|
sys.exit(1)
|
|
except json.JSONDecodeError as e:
|
|
print(f"Invalid JSON in plugin manifest: {path}: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
version = data.get("version")
|
|
if not isinstance(version, str) or not version:
|
|
print(f'Invalid or missing "version" in plugin manifest: {path}', file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
print(version)
|
|
PY
|
|
)"
|
|
|
|
if [ "$skill_version" != "$plugin_version" ]; then
|
|
echo "version mismatch: SKILL.md=$skill_version plugin.json=$plugin_version" >&2
|
|
echo "Update plugin.json \"version\" to match SKILL.md frontmatter." >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "synced: plugin skill + version ($skill_version)"
|