diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ad42bd8..50ddb26 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -13,7 +13,6 @@ - [ ] Ran `uv run python -m pytest -q --tb=short` -- [ ] Ran `bash scripts/sync.sh` (if scripts/ changed) ## Related Issues diff --git a/CHANGELOG.md b/CHANGELOG.md index dfd2eb2..cdf4f4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### 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 diff --git a/CLAUDE.md b/CLAUDE.md index 410e382..a48197a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,12 +12,12 @@ Python scripts with multi-source search aggregation. ## Commands ```bash 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 - `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`) ## Beta channel diff --git a/HERMES_SETUP.md b/HERMES_SETUP.md index ce0d36b..f72d667 100644 --- a/HERMES_SETUP.md +++ b/HERMES_SETUP.md @@ -10,28 +10,20 @@ This guide covers installing last30days on Hermes AI Agent. ## Installation -### Option 1: Via sync.sh (Recommended) - ```bash -# Clone the repo -git clone https://github.com/mvanhorn/last30days-skill.git -cd last30days-skill - -# Run the sync script -bash skills/last30days/scripts/sync.sh +hermes skills install mvanhorn/last30days-skill --force ``` -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 -# Create directory -mkdir -p ~/.hermes/skills/research/last30days - -# Copy files -cp skills/last30days/SKILL.md ~/.hermes/skills/research/last30days/ -cp -r skills/last30days/scripts ~/.hermes/skills/research/last30days/ +git clone https://github.com/mvanhorn/last30days-skill.git +mkdir -p ~/.hermes/skills/research +ln -s "$(pwd)/last30days-skill/skills/last30days" ~/.hermes/skills/research/last30days ``` ## Usage @@ -106,14 +98,12 @@ python3.12 scripts/last30days.py --diagnose ## Updating -To update to the latest version: - ```bash -cd last30days-skill -git pull -bash skills/last30days/scripts/sync.sh +hermes skills install mvanhorn/last30days-skill --force ``` +If you symlinked your working tree (developer alternative above), just `git pull` in the repo — edits propagate live, no re-install step. + ## Support - Original repo: https://github.com/mvanhorn/last30days-skill diff --git a/README.md b/README.md index 3ed5c3e..c2758e4 100644 --- a/README.md +++ b/README.md @@ -14,21 +14,18 @@ This README tracks the current v3 pipeline. The runtime skill spec lives in [SKILL.md](SKILL.md), which is the source of truth for the latest command and setup behavior. -Claude Code: +**Claude Code (recommended — auto-updates via marketplace):** ``` /plugin marketplace add mvanhorn/last30days-skill ``` -OpenClaw: +**Codex, Cursor, Copilot, Gemini CLI, or any of 50+ [Agent Skills](https://agentskills.io) hosts:** ``` -clawhub install last30days-official +npx skills add mvanhorn/last30days-skill -g ``` +(`-g` installs globally for your user, available across all projects. Drop it to scope per-project.) -Hermes: -``` -# The skill auto-deploys when you run sync.sh -# Or manually copy to ~/.hermes/skills/research/last30days/ -``` +More install options (claude.ai web, OpenClaw, manual) in the [Install](#install) section below. Zero config. Reddit, HN, Polymarket, and GitHub work immediately. Run it once and the setup wizard unlocks X, YouTube, TikTok, and more in 30 seconds. @@ -168,12 +165,61 @@ Say "eli5 on" after any research run. The synthesis rewrites in plain language. ## Install -| Surface | Install | -|---------|---------| -| **claude.ai** (web) | [Download `last30days.skill`](https://github.com/mvanhorn/last30days-skill/releases/latest/download/last30days.skill) and upload via Settings > Capabilities > Skills > + | -| **Claude Code** | `/plugin marketplace add mvanhorn/last30days-skill` | -| **OpenClaw** | `clawhub install last30days-official` | -| **Gemini CLI** | Clone then `gemini extensions install ./last30days-skill` (see below) | +| Surface | Install | Updates | +|---------|---------|---------| +| **Claude Code** (recommended) | `/plugin marketplace add mvanhorn/last30days-skill` | Auto via marketplace, or `claude plugin update last30days@last30days-skill` | +| **Codex, Cursor, Copilot, Gemini CLI, GitHub Copilot, or any of 50+ [Agent Skills](https://agentskills.io) hosts** | `npx skills add mvanhorn/last30days-skill -g` | `npx skills update last30days -g` | +| **claude.ai** (web) | [Download `last30days.skill`](https://github.com/mvanhorn/last30days-skill/releases/latest/download/last30days.skill) and upload via Settings > Capabilities > Skills > + | Re-download and re-upload | +| **OpenClaw** | `clawhub install last30days-official` | `clawhub update last30days-official` | + +### Claude Code (recommended) + +``` +/plugin marketplace add mvanhorn/last30days-skill +``` + +Recommended because the Claude Code marketplace handles updates for you — the plugin cache is versioned and auto-refreshes when a new release publishes. Run `claude plugin update last30days@last30days-skill` to force a check. + +If you'd rather use the agent-skills install path on Claude Code, that's also supported: + +``` +npx skills add mvanhorn/last30days-skill -g -a claude-code +``` + +The native plugin and the `npx skills` install can coexist; Claude Code dedupes the slash command. + +### Codex, Cursor, Copilot, Gemini CLI, and other Agent Skills hosts + +Install via the open [Agent Skills](https://agentskills.io) CLI — supports 50+ harnesses including `codex`, `cursor`, `github-copilot`, `gemini-cli`, `claude-code`, `windsurf`, `cline`, `continue`, `roo`, `aider-desk`, `opencode`, `goose`, and more (full list on the [vercel-labs/skills repo](https://github.com/vercel-labs/skills)). + +```bash +npx skills add mvanhorn/last30days-skill -g +``` + +The `-g` (global) flag installs to your user directory so the skill is available across all projects. Without `-g`, `npx skills` installs project-locally into `./.skills/` (committed with the repo). For a research-the-world tool, global is what you want. + +By default this installs for whichever harness `npx skills` detects. To target a specific one (or multiple): + +```bash +npx skills add mvanhorn/last30days-skill -g -a codex +npx skills add mvanhorn/last30days-skill -g -a cursor +npx skills add mvanhorn/last30days-skill -g -a gemini-cli +npx skills add mvanhorn/last30days-skill -g -a codex -a cursor +``` + +Update later with: + +```bash +npx skills update last30days -g +``` + +Or update everything you've installed globally via `npx skills`: + +```bash +npx skills update -g +``` + +List and remove with `npx skills list -g` and `npx skills remove last30days -g`. ### claude.ai (web) @@ -181,15 +227,7 @@ Say "eli5 on" after any research run. The synthesis rewrites in plain language. 2. Go to [claude.ai Settings > Capabilities > Skills](https://claude.ai/settings/capabilities) 3. Click the `+` button in the Skills panel and drop the file in -Enable "Code execution and file creation" under Capabilities first - skills won't run without it. - -### Claude Code - -``` -/plugin marketplace add mvanhorn/last30days-skill -``` - -Update later with `claude plugin update last30days@last30days-skill`. +Enable "Code execution and file creation" under Capabilities first — skills won't run without it. ### OpenClaw @@ -197,22 +235,14 @@ Update later with `claude plugin update last30days@last30days-skill`. clawhub install last30days-official ``` -### Gemini CLI - -Gemini CLI v0.9.0 has an upstream installer bug that can fail with `Configuration file not found at /tmp/gemini-extensionXXXXXX/gemini-extension.json` ([upstream issue](https://github.com/google-gemini/gemini-cli/issues/11452)). Workaround: - -```bash -git clone https://github.com/mvanhorn/last30days-skill -gemini extensions install ./last30days-skill -``` - ### Manual (developer) ```bash -git clone https://github.com/mvanhorn/last30days-skill.git ~/.claude/skills/last30days +git clone https://github.com/mvanhorn/last30days-skill.git +ln -s "$(pwd)/last30days-skill/skills/last30days" ~/.claude/skills/last30days ``` -Or build the claude.ai `.skill` file from source: `bash skills/last30days/scripts/build-skill.sh` produces `dist/last30days.skill`. +The symlink keeps the install in sync with your working tree as you edit — no re-copy needed. For `claude.ai`, build the `.skill` file from source: `bash skills/last30days/scripts/build-skill.sh` produces `dist/last30days.skill`. Reddit (with comments), Hacker News, Polymarket, and GitHub work immediately. Zero configuration. Run `/last30days` once and the setup wizard unlocks more sources in 30 seconds. diff --git a/skills/last30days/scripts/lib/render.py b/skills/last30days/scripts/lib/render.py index 7e4aeff..f8f1541 100644 --- a/skills/last30days/scripts/lib/render.py +++ b/skills/last30days/scripts/lib/render.py @@ -21,9 +21,11 @@ _VERSION_RE = re.compile( 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. + Per-harness skill install dirs (`~/.claude/skills`, `~/.codex/skills`, `~/.agents/skills`, + Hermes, etc.) do not always carry `.claude-plugin/plugin.json` — that file ships with + 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 (continue, not break). YAML frontmatter accepts double-quoted, single-quoted, or diff --git a/skills/last30days/scripts/sync.sh b/skills/last30days/scripts/sync.sh deleted file mode 100755 index 09a7e08..0000000 --- a/skills/last30days/scripts/sync.sh +++ /dev/null @@ -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." diff --git a/tests/test_version_consistency.py b/tests/test_version_consistency.py index 47d0255..e7fef5d 100644 --- a/tests/test_version_consistency.py +++ b/tests/test_version_consistency.py @@ -21,14 +21,6 @@ class TestVersionConsistency(unittest.TestCase): version = _skill_version() 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: skill_text = (SKILL_ROOT / "SKILL.md").read_text(encoding="utf-8") compare_text = (SKILL_ROOT / "scripts" / "compare.sh").read_text(encoding="utf-8")