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:
@@ -13,7 +13,6 @@
|
||||
<!-- How did you verify this works? -->
|
||||
|
||||
- [ ] Ran `uv run python -m pytest -q --tb=short`
|
||||
- [ ] Ran `bash scripts/sync.sh` (if scripts/ changed)
|
||||
|
||||
## Related Issues
|
||||
|
||||
|
||||
+2
-1
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+11
-21
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
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")
|
||||
|
||||
Reference in New Issue
Block a user