refactor: delete sync.sh, dev workflow moves to npx skills add . -g -y + native installers
Every job sync.sh did has a better replacement: - Per-harness skill dirs (~/.claude/skills, ~/.codex/skills, ~/.agents/skills): `npx skills add . -g -y` writes to every detected harness's home dir and uses symlinks by default. Edits propagate live — no re-deploy step. - Hermes (~/.hermes/skills/research/last30days): `hermes skills install mvanhorn/last30days-skill --force` pulls from GitHub and handles the deploy itself. The script wrapping was redundant. - OpenClaw variant: `clawhub install last30days-official` is what users already run per the README; the maintainer doesn't need a separate variant-deploy step in the public repo's scripts. - Claude marketplace cache (~/.claude/plugins/cache/...): this was a "test against the official install path" hack we shouldn't have been recommending. With PR #400's resolver collapse, STEP 0 no longer enforces the cache as the only valid SKILL.md location. Just install the skill normally via `npx skills` or the marketplace. Cleanup: - DELETE skills/last30days/scripts/sync.sh - tests/test_version_consistency.py — drop test_sync_cache_path_uses_skill_version - CLAUDE.md — replace the sync.sh command + rule with `npx skills add . -g -y` - HERMES_SETUP.md — Installation now uses `hermes skills install --force`; developer-alternative section shows the symlink pattern for live editing - render.py — _skill_version docstring no longer attributes the ".claude-plugin absent" case to sync.sh; explains it via per-harness install paths in general - .github/PULL_REQUEST_TEMPLATE.md — drop the "Ran bash scripts/sync.sh" checklist item CHANGELOG and historical docs (release notes, plan files) keep their existing sync.sh mentions as accurate history.
This commit is contained in:
@@ -13,7 +13,6 @@
|
|||||||
<!-- How did you verify this works? -->
|
<!-- How did you verify this works? -->
|
||||||
|
|
||||||
- [ ] Ran `uv run python -m pytest -q --tb=short`
|
- [ ] Ran `uv run python -m pytest -q --tb=short`
|
||||||
- [ ] Ran `bash scripts/sync.sh` (if scripts/ changed)
|
|
||||||
|
|
||||||
## Related Issues
|
## Related Issues
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -17,7 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- **BREAKING for Codex native-plugin users:** `.codex-plugin/plugin.json` and the matching SKILL_ROOT resolver branch in SKILL.md Step 1. Codex users should install via `npx skills add mvanhorn/last30days-skill` or copy the skill to `~/.codex/skills/last30days/` (which `sync.sh` already writes to in this repo).
|
- **BREAKING for Codex native-plugin users:** `.codex-plugin/plugin.json` and the matching SKILL_ROOT resolver branch in SKILL.md Step 1. Codex users should install via `npx skills add mvanhorn/last30days-skill` or copy the skill to `~/.codex/skills/last30days/`.
|
||||||
|
- **`skills/last30days/scripts/sync.sh`.** The maintainer dev-deploy script is gone. Every job it did has a better replacement: `npx skills add . -g -y` symlinks the working tree into every detected harness's skill dir (better than sync.sh's copy model — edits propagate live), `hermes skills install mvanhorn/last30days-skill --force` handles Hermes, `clawhub install last30days-official` handles OpenClaw, and the Claude marketplace cache target was a "test against the official install path" hack we shouldn't have been recommending in the first place. The `test_sync_cache_path_uses_skill_version` test was dropped along with it. CLAUDE.md, HERMES_SETUP.md, the PR template, and a render.py docstring were updated to drop references; CHANGELOG and historical docs (release notes, plan files) keep their existing mentions as accurate history.
|
||||||
|
|
||||||
## [3.2.0] - 2026-05-09
|
## [3.2.0] - 2026-05-09
|
||||||
|
|
||||||
|
|||||||
@@ -12,12 +12,12 @@ Python scripts with multi-source search aggregation.
|
|||||||
## Commands
|
## Commands
|
||||||
```bash
|
```bash
|
||||||
python3 skills/last30days/scripts/last30days.py "test query" --emit=compact
|
python3 skills/last30days/scripts/last30days.py "test query" --emit=compact
|
||||||
bash skills/last30days/scripts/sync.sh
|
npx skills add . -g -y # one-time: symlink this repo into every detected harness's skill dir
|
||||||
```
|
```
|
||||||
|
|
||||||
## Rules
|
## Rules
|
||||||
- `lib/__init__.py` must be bare package marker (comment only, NO eager imports)
|
- `lib/__init__.py` must be bare package marker (comment only, NO eager imports)
|
||||||
- After edits: run `bash skills/last30days/scripts/sync.sh` to deploy
|
- One-time setup: `npx skills add . -g -y` creates symlinks from each detected harness's skill dir to this repo. Edits in the working tree propagate live to every harness — no re-deploy step needed.
|
||||||
- Git remote: origin = public (`mvanhorn/last30days-skill`)
|
- Git remote: origin = public (`mvanhorn/last30days-skill`)
|
||||||
|
|
||||||
## Beta channel
|
## Beta channel
|
||||||
|
|||||||
+11
-21
@@ -10,28 +10,20 @@ This guide covers installing last30days on Hermes AI Agent.
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Option 1: Via sync.sh (Recommended)
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone the repo
|
hermes skills install mvanhorn/last30days-skill --force
|
||||||
git clone https://github.com/mvanhorn/last30days-skill.git
|
|
||||||
cd last30days-skill
|
|
||||||
|
|
||||||
# Run the sync script
|
|
||||||
bash skills/last30days/scripts/sync.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This will auto-detect Hermes and deploy to `~/.hermes/skills/research/last30days/`
|
This pulls the latest release from GitHub and deploys to `~/.hermes/skills/research/last30days/`. `--force` reinstalls over any existing copy.
|
||||||
|
|
||||||
### Option 2: Manual Copy
|
### Developer / live-edit alternative
|
||||||
|
|
||||||
|
If you're hacking on the skill locally and want edits to propagate to Hermes without re-installing, symlink your working tree:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Create directory
|
git clone https://github.com/mvanhorn/last30days-skill.git
|
||||||
mkdir -p ~/.hermes/skills/research/last30days
|
mkdir -p ~/.hermes/skills/research
|
||||||
|
ln -s "$(pwd)/last30days-skill/skills/last30days" ~/.hermes/skills/research/last30days
|
||||||
# Copy files
|
|
||||||
cp skills/last30days/SKILL.md ~/.hermes/skills/research/last30days/
|
|
||||||
cp -r skills/last30days/scripts ~/.hermes/skills/research/last30days/
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -106,14 +98,12 @@ python3.12 scripts/last30days.py --diagnose
|
|||||||
|
|
||||||
## Updating
|
## Updating
|
||||||
|
|
||||||
To update to the latest version:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd last30days-skill
|
hermes skills install mvanhorn/last30days-skill --force
|
||||||
git pull
|
|
||||||
bash skills/last30days/scripts/sync.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you symlinked your working tree (developer alternative above), just `git pull` in the repo — edits propagate live, no re-install step.
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
- Original repo: https://github.com/mvanhorn/last30days-skill
|
- Original repo: https://github.com/mvanhorn/last30days-skill
|
||||||
|
|||||||
@@ -21,9 +21,11 @@ _VERSION_RE = re.compile(
|
|||||||
def _skill_version() -> str:
|
def _skill_version() -> str:
|
||||||
"""Read plugin version from .claude-plugin/plugin.json, falling back to SKILL.md frontmatter.
|
"""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,
|
Per-harness skill install dirs (`~/.claude/skills`, `~/.codex/skills`, `~/.agents/skills`,
|
||||||
~/.agents/skills, Hermes), so SKILL.md frontmatter is the fallback that keeps the
|
Hermes, etc.) do not always carry `.claude-plugin/plugin.json` — that file ships with
|
||||||
badge from emitting v? on those installs. Returns "?" only if both sources are missing.
|
plugin-cache installs but not with per-harness skill installs. SKILL.md frontmatter is
|
||||||
|
the fallback that keeps the badge from emitting v? on those installs. Returns "?" only
|
||||||
|
if both sources are missing.
|
||||||
|
|
||||||
A corrupt manifest at one ancestor does not shadow a valid manifest at a deeper one
|
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
|
(continue, not break). YAML frontmatter accepts double-quoted, single-quoted, or
|
||||||
|
|||||||
@@ -1,126 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# sync.sh - Deploy last30days skill to all host locations
|
|
||||||
# Usage: bash skills/last30days/scripts/sync.sh (run from repo root)
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
SRC="$(cd "$(dirname "$0")/.." && pwd)"
|
|
||||||
echo "Source: $SRC"
|
|
||||||
|
|
||||||
COMMON_TARGETS=(
|
|
||||||
# Claude Code plugin cache for this (public) repo's marketplace install.
|
|
||||||
# Marketplace name = last30days-skill (.claude-plugin/marketplace.json).
|
|
||||||
# Plugin name = last30days (.claude-plugin/plugin.json).
|
|
||||||
# Path shape = .../cache/{marketplace-name}/{plugin-name}/{version}.
|
|
||||||
# Marketplace pulls overwrite this on update; local sync keeps it fresh
|
|
||||||
# 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.3"
|
|
||||||
"$HOME/.agents/skills/last30days"
|
|
||||||
"$HOME/.codex/skills/last30days"
|
|
||||||
)
|
|
||||||
OPENCLAW_TARGET="$HOME/.openclaw/skills/last30days"
|
|
||||||
|
|
||||||
sync_target() {
|
|
||||||
local target="$1"
|
|
||||||
local skill_md="$2"
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "--- Syncing to $target ---"
|
|
||||||
mkdir -p "$target/scripts/lib"
|
|
||||||
|
|
||||||
cp "$skill_md" "$target/SKILL.md"
|
|
||||||
|
|
||||||
rsync -a \
|
|
||||||
"$SRC/scripts/last30days.py" \
|
|
||||||
"$SRC/scripts/watchlist.py" \
|
|
||||||
"$SRC/scripts/briefing.py" \
|
|
||||||
"$SRC/scripts/store.py" \
|
|
||||||
"$target/scripts/"
|
|
||||||
rsync -a "$SRC/scripts/lib/"*.py "$target/scripts/lib/"
|
|
||||||
|
|
||||||
# The OpenClaw variant lives in the private repo only. Skip cleanly when
|
|
||||||
# running this script from the public repo where variants/open does not exist.
|
|
||||||
if [ -d "$SRC/variants/open" ]; then
|
|
||||||
mkdir -p "$target/variants/open/references"
|
|
||||||
rsync -a "$SRC/variants/open/" "$target/variants/open/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -d "$SRC/scripts/lib/vendor" ]; then
|
|
||||||
rsync -a "$SRC/scripts/lib/vendor" "$target/scripts/lib/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -d "$SRC/fixtures" ]; then
|
|
||||||
mkdir -p "$target/fixtures"
|
|
||||||
rsync -a "$SRC/fixtures/" "$target/fixtures/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
mod_count=$(ls "$target/scripts/lib/"*.py 2>/dev/null | wc -l | tr -d ' ')
|
|
||||||
echo " Copied $mod_count modules"
|
|
||||||
|
|
||||||
if (
|
|
||||||
cd "$target/scripts" &&
|
|
||||||
python3 -c "import briefing, store, watchlist; from lib import youtube_yt, bird_x, render, ui; print(' Import check: OK')"
|
|
||||||
); then
|
|
||||||
true
|
|
||||||
else
|
|
||||||
echo " Import check FAILED"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
for t in "${COMMON_TARGETS[@]}"; do
|
|
||||||
sync_target "$t" "$SRC/SKILL.md"
|
|
||||||
done
|
|
||||||
|
|
||||||
# Hermes sync: deploy to Hermes skills directory if it exists
|
|
||||||
HERMES_TARGET="$HOME/.hermes/skills/research/last30days"
|
|
||||||
if [ -d "$HOME/.hermes/skills/research" ]; then
|
|
||||||
echo ""
|
|
||||||
echo "--- Syncing to Hermes ---"
|
|
||||||
mkdir -p "$HERMES_TARGET/scripts/lib"
|
|
||||||
|
|
||||||
cp "$SRC/SKILL.md" "$HERMES_TARGET/SKILL.md"
|
|
||||||
|
|
||||||
rsync -a \
|
|
||||||
"$SRC/scripts/last30days.py" \
|
|
||||||
"$SRC/scripts/watchlist.py" \
|
|
||||||
"$SRC/scripts/briefing.py" \
|
|
||||||
"$SRC/scripts/store.py" \
|
|
||||||
"$HERMES_TARGET/scripts/"
|
|
||||||
rsync -a "$SRC/scripts/lib/"*.py "$HERMES_TARGET/scripts/lib/"
|
|
||||||
|
|
||||||
if [ -d "$SRC/scripts/lib/vendor" ]; then
|
|
||||||
rsync -a "$SRC/scripts/lib/vendor" "$HERMES_TARGET/scripts/lib/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -d "$SRC/fixtures" ]; then
|
|
||||||
mkdir -p "$HERMES_TARGET/fixtures"
|
|
||||||
rsync -a "$SRC/fixtures/" "$HERMES_TARGET/fixtures/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
mod_count=$(ls "$HERMES_TARGET/scripts/lib/"*.py 2>/dev/null | wc -l | tr -d ' ')
|
|
||||||
echo " Copied $mod_count modules to Hermes"
|
|
||||||
|
|
||||||
if (
|
|
||||||
cd "$HERMES_TARGET/scripts" &&
|
|
||||||
python3 -c "import briefing, store, watchlist; from lib import youtube_yt, bird_x, render, ui; print(' Import check: OK')"
|
|
||||||
); then
|
|
||||||
true
|
|
||||||
else
|
|
||||||
echo " Import check FAILED"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# OpenClaw sync only runs when the private-repo OpenClaw variant is present
|
|
||||||
# in the source tree. The public repo does not ship variants/open (the variant
|
|
||||||
# is sanitized via strip_for_openclaw.py and published separately from
|
|
||||||
# last30days-skill-private).
|
|
||||||
if [ -d "$SRC/variants/open" ]; then
|
|
||||||
sync_target "$OPENCLAW_TARGET" "$SRC/variants/open/SKILL.md"
|
|
||||||
else
|
|
||||||
echo ""
|
|
||||||
echo "Skipping OpenClaw target (no variants/open in this repo)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "Sync complete."
|
|
||||||
@@ -21,14 +21,6 @@ class TestVersionConsistency(unittest.TestCase):
|
|||||||
version = _skill_version()
|
version = _skill_version()
|
||||||
self.assertIn(f"# last30days v{version}:", text)
|
self.assertIn(f"# last30days v{version}:", text)
|
||||||
|
|
||||||
def test_sync_cache_path_uses_skill_version(self) -> None:
|
|
||||||
sync_text = (SKILL_ROOT / "scripts" / "sync.sh").read_text(encoding="utf-8")
|
|
||||||
version = _skill_version()
|
|
||||||
self.assertIn(
|
|
||||||
f'last30days-skill/last30days/{version}"',
|
|
||||||
sync_text,
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_memory_save_dir_uses_single_env_variable(self) -> None:
|
def test_memory_save_dir_uses_single_env_variable(self) -> None:
|
||||||
skill_text = (SKILL_ROOT / "SKILL.md").read_text(encoding="utf-8")
|
skill_text = (SKILL_ROOT / "SKILL.md").read_text(encoding="utf-8")
|
||||||
compare_text = (SKILL_ROOT / "scripts" / "compare.sh").read_text(encoding="utf-8")
|
compare_text = (SKILL_ROOT / "scripts" / "compare.sh").read_text(encoding="utf-8")
|
||||||
|
|||||||
Reference in New Issue
Block a user