Fix skill not executing: remove agent:Explore, front-load run command

The Explore agent was treating SKILL.md as documentation to summarize
instead of instructions to follow. Removed agent:Explore from frontmatter
and restructured opening to immediately command script execution.

Also adds release plan for Bird CLI v2.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt Van Horn
2026-02-06 09:38:32 -08:00
parent 4230fa2b69
commit 63de326df0
2 changed files with 401 additions and 85 deletions
+10 -85
View File
@@ -1,31 +1,22 @@
--- ---
name: last30days name: last30days
description: Research a topic from the last 30 days on Reddit + X + Web with Bird CLI support. description: last30days: Research Any Topic from the Last 30 Days (user)
argument-hint: '"[topic] for [tool]" or "[topic]"' argument-hint: '"[topic] for [tool]" or "[topic]"'
context: fork context: fork
agent: Explore
disable-model-invocation: true
allowed-tools: Bash, Read, Write, AskUserQuestion, WebSearch allowed-tools: Bash, Read, Write, AskUserQuestion, WebSearch
--- ---
# last30days: Research the Last 30 Days # STEP 1: RUN THE RESEARCH SCRIPT IMMEDIATELY
Research ANY topic across Reddit, X, and the web. Surface what people are actually discussing, recommending, and debating right now. Do NOT describe this skill. Do NOT summarize workflows. Do NOT explain what you will do. EXECUTE.
**Features:** The user's arguments are in $ARGUMENTS. Run this command NOW:
- Bird CLI support for free X/Twitter search (no API key needed)
- Auto-detects Bird if installed (no prompts)
- Falls back to xAI API or WebSearch if Bird not available
Use cases: ```bash
- **Prompting**: "photorealistic people in Nano Banana Pro", "Midjourney prompts", "ChatGPT image generation" → learn techniques, get copy-paste prompts python3 ~/.claude/skills/last30days/scripts/last30days.py "$ARGUMENTS" --emit=compact 2>&1
- **Recommendations**: "best Claude Code skills", "top AI tools" → get a LIST of specific things people mention ```
- **News**: "what's happening with OpenAI", "latest AI announcements" → current events and updates
- **General**: any topic you're curious about → understand what the community is saying
## CRITICAL: Parse User Intent While that runs, parse the user's input for:
Before doing anything, parse the user's input for:
1. **TOPIC**: What they want to learn about (e.g., "web app mockups", "Claude Code skills", "image generation") 1. **TOPIC**: What they want to learn about (e.g., "web app mockups", "Claude Code skills", "image generation")
2. **TARGET TOOL** (if specified): Where they'll use the prompts (e.g., "Nano Banana Pro", "ChatGPT", "Midjourney") 2. **TARGET TOOL** (if specified): Where they'll use the prompts (e.g., "Nano Banana Pro", "ChatGPT", "Midjourney")
@@ -53,75 +44,9 @@ Common patterns:
--- ---
## Setup Check ## STEP 2: DO WEBSEARCH WHILE SCRIPT RUNS
The skill works in multiple modes based on available sources: The script auto-detects sources (Bird CLI, API keys, etc). While waiting for it, do WebSearch.
1. **Bird Mode** (free): X via Bird CLI + Reddit via OpenAI + WebSearch
2. **Full Mode** (both API keys): Reddit + X via xAI + WebSearch
3. **Partial Mode** (one key): Reddit-only or X-only + WebSearch
4. **Web-Only Mode** (no keys): WebSearch only
**Bird CLI is the preferred X source** - free, uses your browser session.
### First-Time Setup
**Option 1: Install Bird CLI (Recommended - Free X search)**
```bash
npm install -g @steipete/bird
```
Then log into X (twitter.com) in your browser. Bird uses your browser session.
The script auto-detects Bird if installed.
**Option 2: API Keys (Optional)**
If you want API-based access:
```bash
mkdir -p ~/.config/last30days
cat > ~/.config/last30days/.env << 'ENVEOF'
# last30days API Configuration
# All keys are optional - Bird CLI or WebSearch fallback available
# For Reddit research (uses OpenAI's web_search tool)
OPENAI_API_KEY=
# For X/Twitter research (uses xAI's x_search tool - fallback if no Bird)
XAI_API_KEY=
ENVEOF
chmod 600 ~/.config/last30days/.env
```
**DO NOT stop if no keys are configured.** Proceed with available sources.
---
## Research Execution
**IMPORTANT: The script handles source detection automatically.** Run it and check the output.
**Step 1: Run the research script**
```bash
python3 ~/.claude/skills/last30days/scripts/last30days.py "$ARGUMENTS" --emit=compact 2>&1
```
The script will automatically:
- Check for Bird CLI (free X search)
- Offer to install Bird if not found and npm available
- Detect API keys
- Run Reddit/X searches with best available source
- Signal if WebSearch is needed
**Step 2: Check the output mode**
The script output will indicate the mode:
- **"Mode: both"** - Has both Reddit and X sources
- **"Mode: reddit-only"** or **"Mode: x-only"** - Has one source
- **"Mode: web-only"** - No API keys or Bird, Claude must do ALL research via WebSearch
**Step 3: Do WebSearch**
For **ALL modes**, do WebSearch to supplement (or provide all data in web-only mode). For **ALL modes**, do WebSearch to supplement (or provide all data in web-only mode).
@@ -0,0 +1,391 @@
---
title: "feat: Release last30days v2 with Bird CLI to GitHub"
type: feat
date: 2026-02-06
---
# Release last30days v2 (Bird CLI) to GitHub
## Overview
Replace the current public `last30days-skill` on GitHub with the new Bird CLI-enhanced version from `last30days-skill-private`. The new version adds free X/Twitter search via Bird CLI while maintaining backward compatibility with xAI API keys.
**Goal:** Ship with confidence. No rollbacks.
## Current State
| | Old (Public) | New (Private) |
|---|---|---|
| **Repo** | `mvanhorn/last30days-skill` | `mvanhorn/last30days-skill-private` |
| **Local path** | `~/.claude/skills/last30days/` | `~/.claude/skills/last30daystest/` (symlink) |
| **Remote** | `origin` → public repo | `origin` → private, `upstream` → public |
| **Key addition** | -- | Bird CLI (`@steipete/bird`) for free X search |
| **X source chain** | xAI API only | Bird (free) → xAI (paid) → WebSearch |
| **Uses** | 136 | 18 |
| **Latest commit** | `cc892d7` | `4230fa2` |
**Why both show as `/last30days`:** Both `SKILL.md` files declare `name: last30days` in frontmatter. Claude Code discovers both from `~/.claude/skills/` and lists them separately.
---
## Phase 0: Clean Swap (Day 1)
Remove the old skill so only the new one is active. This eliminates ambiguity during testing.
### Steps
1. **Back up the old skill** (safety net):
```bash
mv ~/.claude/skills/last30days ~/.claude/skills/last30days.backup-v1
```
2. **Promote the new skill to primary**:
```bash
# Remove the test symlink
rm ~/.claude/skills/last30daystest
# Create new symlink with the primary name
ln -s /Users/mvanhorn/last30days-skill-private ~/.claude/skills/last30days
```
3. **Verify only one `/last30days` appears**:
- Open a new Claude Code session
- Type `/last` and confirm only ONE `/last30days` shows in autocomplete
- Confirm description mentions Bird CLI
4. **Rollback procedure** (if something goes wrong):
```bash
rm ~/.claude/skills/last30days
mv ~/.claude/skills/last30days.backup-v1 ~/.claude/skills/last30days
```
### Acceptance Criteria
- [ ] Only one `/last30days` appears in Claude Code autocomplete
- [ ] Old skill preserved at `~/.claude/skills/last30days.backup-v1`
- [ ] New skill responds to `/last30days` invocation
---
## Phase 1: Claude's Test Plan (Automated)
These are tests Claude can run autonomously to validate the new skill before the user touches it.
### 1.1 Script-Level Smoke Tests
Run the Python scripts directly to verify core functionality without invoking the full skill.
#### Bird CLI Detection
```bash
# Test: Bird is installed and authenticated
python3 -c "
import sys; sys.path.insert(0, '/Users/mvanhorn/last30days-skill-private/scripts/lib')
import bird_x
print('installed:', bird_x.is_bird_installed())
print('authenticated:', bird_x.is_bird_authenticated())
print('status:', bird_x.get_bird_status())
"
```
- [ ] `is_bird_installed()` returns True (or False with clear message)
- [ ] `is_bird_authenticated()` returns True if logged into X in browser
- [ ] `get_bird_status()` returns a dict with `installed`, `authenticated`, `available` keys
#### Environment & Source Detection
```bash
python3 -c "
import sys; sys.path.insert(0, '/Users/mvanhorn/last30days-skill-private/scripts/lib')
import env
config = env.load_config()
print('x_source:', env.get_x_source(config))
print('has_openai:', bool(config.get('OPENAI_API_KEY')))
"
```
- [ ] `get_x_source()` returns `'bird'` if Bird available, `'xai'` if API key set, `None` otherwise
- [ ] Config loads from `~/.config/last30days/.env`
#### Bird Search (Direct)
```bash
python3 -c "
import sys, json; sys.path.insert(0, '/Users/mvanhorn/last30days-skill-private/scripts/lib')
import bird_x
result = bird_x.search_x('Claude Code tips', '2026-01-07', '2026-02-06', 'quick')
print(json.dumps(result, indent=2, default=str)[:2000])
"
```
- [ ] Returns search results (list of dicts with `url`, `text`, `author_handle`)
- [ ] No Python tracebacks
- [ ] Results are from the expected date range
#### Full Research Pipeline (Compact Output)
```bash
cd /Users/mvanhorn/last30days-skill-private
python3 scripts/last30days.py "Claude Code tips" --emit=compact --quick 2>&1 | head -100
```
- [ ] Completes without error
- [ ] Output includes X results (via Bird or xAI)
- [ ] Output includes Reddit results (via OpenAI) if key configured
- [ ] Stats summary shows source counts
### 1.2 Source Fallback Tests
Verify graceful degradation when sources are unavailable.
#### Bird unavailable, xAI available
```bash
# Temporarily hide Bird
PATH_BACKUP="$PATH"
export PATH=$(echo "$PATH" | tr ':' '\n' | grep -v "$(dirname $(which bird 2>/dev/null))" | tr '\n' ':')
python3 -c "
import sys; sys.path.insert(0, '/Users/mvanhorn/last30days-skill-private/scripts/lib')
import env
config = env.load_config()
print('x_source (no bird):', env.get_x_source(config))
"
export PATH="$PATH_BACKUP"
```
- [ ] Falls back to `'xai'` when Bird not in PATH
- [ ] No crash or unhandled exception
#### No X source at all
```bash
python3 -c "
import sys; sys.path.insert(0, '/Users/mvanhorn/last30days-skill-private/scripts/lib')
import env
config = {} # empty config, no keys
print('x_source (nothing):', env.get_x_source(config))
"
```
- [ ] Returns `None`
- [ ] No crash
### 1.3 Response Parsing Tests
Validate that Bird responses are correctly normalized to the canonical schema.
```bash
python3 -c "
import sys; sys.path.insert(0, '/Users/mvanhorn/last30days-skill-private/scripts/lib')
import bird_x
# Test with sample Bird response format
sample = {
'tweets': [{
'permanentUrl': 'https://x.com/user/status/123',
'text': 'Test tweet about Claude Code',
'username': 'testuser',
'likeCount': 42,
'retweetCount': 10,
'replyCount': 5,
'timeParsed': '2026-02-01T12:00:00.000Z'
}]
}
parsed = bird_x.parse_bird_response(sample)
print('Parsed count:', len(parsed))
print('First item keys:', sorted(parsed[0].keys()) if parsed else 'EMPTY')
print('URL:', parsed[0].get('url'))
print('Author:', parsed[0].get('author_handle'))
"
```
- [ ] Parses correctly with expected keys
- [ ] Handles both camelCase and snake_case fields
- [ ] URL, text, author, engagement metrics all present
### 1.4 SKILL.md Validation
```bash
# Verify YAML frontmatter parses correctly
python3 -c "
import yaml
with open('/Users/mvanhorn/last30days-skill-private/SKILL.md') as f:
content = f.read()
# Extract YAML between --- markers
parts = content.split('---', 2)
meta = yaml.safe_load(parts[1])
print('name:', meta.get('name'))
print('context:', meta.get('context'))
print('agent:', meta.get('agent'))
print('allowed-tools:', meta.get('allowed-tools'))
"
```
- [ ] `name` is `last30days` (not `last30daystest`)
- [ ] `context` is `fork`
- [ ] `agent` is `Explore`
- [ ] `allowed-tools` includes `Bash`, `WebSearch`
### 1.5 Diff Audit (Old vs New)
```bash
# Verify the only meaningful addition is bird_x.py
diff -rq ~/.claude/skills/last30days.backup-v1/scripts/lib/ \
/Users/mvanhorn/last30days-skill-private/scripts/lib/ 2>/dev/null
```
- [ ] Only new file is `bird_x.py`
- [ ] Modified files: `env.py` (source detection), `__init__.py` (exports)
- [ ] No unexpected deletions or renames
---
## Phase 2: User's Test Plan (Manual)
These require human judgment - evaluating quality, UX, and real-world behavior.
### 2.1 Basic Invocation (5 min)
Open a fresh Claude Code session after Phase 0 is complete.
| # | Test | Command | Pass Criteria |
|---|------|---------|---------------|
| 1 | Simple topic | `/last30days AI music generation` | Returns results, shows source stats |
| 2 | Topic + tool | `/last30days Suno prompts for music production` | Returns results + generates a prompt |
| 3 | Quick mode | `/last30days --quick TypeScript tips` | Faster, fewer results, still valid |
| 4 | Empty input | `/last30days` | Prompts for topic (doesn't crash) |
### 2.2 Bird CLI Verification (5 min)
| # | Test | What to Check |
|---|------|---------------|
| 1 | Source indicator | Output shows Bird as X source (not xAI) |
| 2 | X results quality | X/Twitter results are real, recent, have engagement metrics |
| 3 | Bird promo | If Bird NOT installed, shows non-blocking info banner |
| 4 | Mixed sources | Both Reddit (OpenAI) and X (Bird) results appear |
### 2.3 Fallback Behavior (5 min)
| # | Test | Setup | Expected |
|---|------|-------|----------|
| 1 | No Bird | `npm uninstall -g @steipete/bird` temporarily | Falls back to xAI or WebSearch |
| 2 | No API keys | Rename `~/.config/last30days/.env` temporarily | WebSearch-only mode works |
| 3 | Restore | Reinstall bird + restore .env | Full mode returns |
### 2.4 Output Quality (10 min)
Run 3 real research queries you care about. For each, evaluate:
- [ ] Results are actually from the last 30 days (not stale)
- [ ] Engagement metrics (likes, upvotes) are present and reasonable
- [ ] No duplicate results
- [ ] Sources are properly cited with URLs
- [ ] Synthesis is grounded in actual results (not hallucinated)
- [ ] Generated prompts (if requested) are usable
### 2.5 Comparison Test (10 min)
Before removing the backup, run the SAME query on both versions:
```bash
# New version (active)
/last30days [your topic]
# Old version (temporarily restore)
rm ~/.claude/skills/last30days
mv ~/.claude/skills/last30days.backup-v1 ~/.claude/skills/last30days
# New Claude Code session
/last30days [same topic]
# Then swap back
```
- [ ] New version produces equal or better results
- [ ] No features regressed
- [ ] Bird results add value over xAI-only
---
## Phase 3: Release Plan (30-Day Timeline)
**Target release date:** March 1, 2026 (conservative buffer before March 8 deadline)
### Week 1: Feb 6-12 - Clean & Test
| Day | Task | Owner |
|-----|------|-------|
| Feb 6 | Phase 0: Clean swap (remove old, activate new) | User |
| Feb 6 | Phase 1: Claude runs automated tests | Claude |
| Feb 7-8 | Phase 2: User runs manual tests (2.1-2.4) | User |
| Feb 9 | Phase 2.5: Comparison test | User |
| Feb 10-12 | Fix any issues found during testing | Claude + User |
### Week 2: Feb 13-19 - Harden
| Day | Task | Owner |
|-----|------|-------|
| Feb 13 | Run edge cases: unicode topics, very long topics, special chars | Claude |
| Feb 14 | Test with Bird logged out (auth expiry scenario) | User |
| Feb 15 | Review all error messages for clarity | Claude |
| Feb 16-17 | Update README.md with Bird CLI setup instructions | Claude |
| Feb 18-19 | Buffer for fixes | Claude + User |
### Week 3: Feb 20-26 - Pre-Release
| Day | Task | Owner |
|-----|------|-------|
| Feb 20 | Final diff audit: private repo vs public repo | Claude |
| Feb 21 | Strip any private/test artifacts (test symlinks, debug prints) | Claude |
| Feb 22 | Update SKILL.md description if needed | Claude |
| Feb 23 | Dry-run: push to a branch on public repo (not main) | User |
| Feb 24 | Test installation from the branch (fresh `~/.claude/skills/`) | User |
| Feb 25-26 | Buffer for fixes | Claude + User |
### Week 4: Feb 27 - Mar 1 - Ship
| Day | Task | Owner |
|-----|------|-------|
| Feb 27 | Merge branch to main on public repo | User |
| Feb 28 | Create GitHub release with changelog | Claude + User |
| Mar 1 | Delete backup: `rm -rf ~/.claude/skills/last30days.backup-v1` | User |
| Mar 1 | Archive private repo (optional) | User |
### Release Checklist (Final Gate)
Before merging to `main` on the public repo:
- [ ] All Phase 1 automated tests pass
- [ ] All Phase 2 manual tests pass
- [ ] Comparison test shows new >= old quality
- [ ] SKILL.md frontmatter is correct (`name: last30days`, not `last30daystest`)
- [ ] README.md documents Bird CLI setup
- [ ] No debug/test artifacts in codebase
- [ ] No hardcoded paths (e.g., `/Users/mvanhorn/...`)
- [ ] `.env` files are gitignored
- [ ] Git history is clean (no "test" or "WIP" commits on main)
- [ ] Bird CLI failure doesn't break the skill (graceful fallback verified)
### Rollback Plan (Emergency)
If something goes wrong after release:
```bash
# Option 1: Revert to backup (if still exists)
rm ~/.claude/skills/last30days
mv ~/.claude/skills/last30days.backup-v1 ~/.claude/skills/last30days
# Option 2: Git revert on public repo
cd ~/.claude/skills/last30days
git log --oneline -5 # find the last good commit
git revert HEAD # revert the merge commit
git push origin main
# Option 3: Pin to old version
cd ~/.claude/skills/last30days
git checkout cc892d7 # last known good commit from old version
```
---
## Risk Analysis
| Risk | Likelihood | Impact | Mitigation |
|------|-----------|--------|------------|
| Bird CLI breaks after X API changes | Medium | Low | Fallback to xAI/WebSearch still works |
| Bird auth expires silently | Medium | Low | `is_bird_authenticated()` check + user message |
| Old xAI workflows regress | Low | High | Comparison test in Phase 2.5 |
| Hardcoded paths in codebase | Low | Medium | Grep for `/Users/mvanhorn` before release |
| SKILL.md name still says `last30daystest` | Low | High | Already fixed in commit `4e972d0` |
## References
- Private repo: `https://github.com/mvanhorn/last30days-skill-private.git`
- Public repo: `https://github.com/mvanhorn/last30days-skill.git`
- Bird CLI: `https://github.com/steipete/bird`
- Bird implementation plan: `docs/plans/2026-02-03-bird-cli-implementation.md`
- Bird integration design: `docs/plans/2026-02-03-bird-cli-integration-design.md`