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>
13 KiB
title, type, date
| title | type | date |
|---|---|---|
| feat: Release last30days v2 with Bird CLI to GitHub | feat | 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
-
Back up the old skill (safety net):
mv ~/.claude/skills/last30days ~/.claude/skills/last30days.backup-v1 -
Promote the new skill to primary:
# 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 -
Verify only one
/last30daysappears:- Open a new Claude Code session
- Type
/lastand confirm only ONE/last30daysshows in autocomplete - Confirm description mentions Bird CLI
-
Rollback procedure (if something goes wrong):
rm ~/.claude/skills/last30days mv ~/.claude/skills/last30days.backup-v1 ~/.claude/skills/last30days
Acceptance Criteria
- Only one
/last30daysappears in Claude Code autocomplete - Old skill preserved at
~/.claude/skills/last30days.backup-v1 - New skill responds to
/last30daysinvocation
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
# 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 browserget_bird_status()returns a dict withinstalled,authenticated,availablekeys
Environment & Source Detection
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,Noneotherwise- Config loads from
~/.config/last30days/.env
Bird Search (Direct)
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)
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
# 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
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.
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
# 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'))
"
nameislast30days(notlast30daystest)contextisforkagentisExploreallowed-toolsincludesBash,WebSearch
1.5 Diff Audit (Old vs New)
# 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:
# 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, notlast30daystest) - README.md documents Bird CLI setup
- No debug/test artifacts in codebase
- No hardcoded paths (e.g.,
/Users/mvanhorn/...) .envfiles 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:
# 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