Merge pull request #405 from tmchow/docs/readme-multi-harness-install

docs+refactor: modernize install story everywhere, delete sync.sh
This commit is contained in:
Trevin Chow
2026-05-15 23:46:57 -07:00
committed by GitHub
8 changed files with 84 additions and 196 deletions
-1
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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
+64 -34
View File
@@ -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. 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 /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: More install options (claude.ai web, OpenClaw, manual) in the [Install](#install) section below.
```
# The skill auto-deploys when you run sync.sh
# Or manually copy to ~/.hermes/skills/research/last30days/
```
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. 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 ## Install
| Surface | Install | | Surface | Install | Updates |
|---------|---------| |---------|---------|---------|
| **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** (recommended) | `/plugin marketplace add mvanhorn/last30days-skill` | Auto via marketplace, or `claude plugin update last30days@last30days-skill` |
| **Claude Code** | `/plugin marketplace add mvanhorn/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` |
| **OpenClaw** | `clawhub install last30days-official` | | **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 |
| **Gemini CLI** | Clone then `gemini extensions install ./last30days-skill` (see below) | | **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) ### 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) 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 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. 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`.
### OpenClaw ### OpenClaw
@@ -197,22 +235,14 @@ Update later with `claude plugin update last30days@last30days-skill`.
clawhub install last30days-official 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) ### Manual (developer)
```bash ```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. 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.
+5 -3
View File
@@ -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
-126
View File
@@ -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."
-8
View File
@@ -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")