Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
12 KiB
title, type, date
| title | type | date |
|---|---|---|
| feat: Merge OpenClaw variant into main repo | feat | 2026-02-14 |
feat: Merge OpenClaw Variant into Main Repo
Overview
Consolidate the last30days-openclaw project into last30days-skill-private so there's one unified Python engine powering both the main skill (Claude Code / Codex) and an "open" variant with watchlist, briefing, history, and built-in web search. The open variant also gets YouTube and Bird CLI — features the main project already has but openclaw was built before they existed.
Problem Statement / Motivation
Right now there are two separate repos with diverging codebases:
last30days-skill-private(main, Feb 14) — YouTube, vendored Bird, better scoring/normalization, Codex compat. But no built-in web search APIs and no persistence layer.last30days-openclaw(Feb 10) — SQLite store, watchlist, briefings, 3 web search backends (Parallel AI, Brave, OpenRouter). But frozen without YouTube or latest engine improvements.
They share ~80% of the same scripts/lib/ files but are drifting apart. Maintaining two codebases is unsustainable.
Goal: One repo, one Python engine, two SKILL.md variants. Install once, works everywhere.
Proposed Solution
Use last30days-skill-private as the base (it's 4 days newer with better code) and port the OpenClaw-exclusive features in:
What gets ported from OpenClaw
| File | What it does | Destination |
|---|---|---|
scripts/store.py |
SQLite research accumulator (WAL, FTS5, dedup) | scripts/store.py |
scripts/watchlist.py |
Topic watchlist CLI (add/remove/list/run) | scripts/watchlist.py |
scripts/briefing.py |
Morning briefing generator (daily/weekly) | scripts/briefing.py |
scripts/lib/brave_search.py |
Brave Search API (free tier, 2K/mo) | scripts/lib/brave_search.py |
scripts/lib/parallel_search.py |
Parallel AI search (LLM-optimized) | scripts/lib/parallel_search.py |
scripts/lib/openrouter_search.py |
OpenRouter/Sonar Pro search | scripts/lib/openrouter_search.py |
references/research.md |
One-shot research instructions | variants/open/references/research.md |
references/watchlist.md |
Watchlist mode instructions | variants/open/references/watchlist.md |
references/briefing.md |
Briefing mode instructions | variants/open/references/briefing.md |
references/history.md |
History query instructions | variants/open/references/history.md |
What gets upgraded in the ported code
store.py: No changes needed — it's self-contained SQLite, works as-iswatchlist.py: Remove OpenClaw cron-specific code, make cron setup generic (launchd on macOS, systemd on Linux, or manual cron)briefing.py: No changes neededscripts/lib/env.py: Merge OpenClaw's web search key support (PARALLEL_API_KEY,BRAVE_API_KEY,OPENROUTER_API_KEY) andhas_web_search_keys()/get_web_search_source()functions into main's env.py. Drop the OpenClaw config loader (~/.openclaw/openclaw.json) — just use env vars and~/.config/last30days/.envscripts/last30days.py: Add OpenClaw's_search_web()function so the script can do web search natively when API keys are available (instead of always delegating to the assistant)
What gets DROPPED from OpenClaw
| File | Why |
|---|---|
scripts/cron_setup.py |
Too OpenClaw-platform-specific. Replace with generic scheduling docs. |
OpenClaw config loader in env.py |
~/.openclaw/openclaw.json path is platform-specific. Use env vars instead. |
.clawhubignore |
OpenClaw marketplace artifact, not needed in unified repo |
New file: Open variant SKILL.md
Create variants/open/SKILL.md — the multi-mode skill with command routing:
variants/open/
├── SKILL.md # Router: watch, briefing, history, or one-shot
├── references/
│ ├── research.md # One-shot research instructions
│ ├── watchlist.md # Watchlist management instructions
│ ├── briefing.md # Briefing mode instructions
│ └── history.md # History query instructions
└── context.md # Agent memory (user preferences, source quality)
The open variant's SKILL.md points to {baseDir}/scripts/last30days.py (same engine) but adds the router and reference file system. It also adds the --store flag for persistence.
How YouTube and Bird CLI get added to the open variant
They're already in scripts/lib/youtube_yt.py and scripts/lib/vendor/bird/. The open variant's SKILL.md just needs to mention YouTube in its description and the research.md reference file gets the YouTube stats line in the output format. No code changes needed — the Python engine already supports all four sources.
Technical Considerations
File Structure After Merge
last30days-skill-private/
├── SKILL.md # Main skill (Claude Code / Codex)
├── agents/openai.yaml # Codex discovery (existing)
├── variants/
│ └── open/
│ ├── SKILL.md # Open variant with routing
│ ├── references/
│ │ ├── research.md
│ │ ├── watchlist.md
│ │ ├── briefing.md
│ │ └── history.md
│ └── context.md
├── scripts/
│ ├── last30days.py # Unified engine (+ native web search)
│ ├── store.py # SQLite accumulator (from openclaw)
│ ├── watchlist.py # Watchlist CLI (from openclaw, genericized)
│ ├── briefing.py # Briefing generator (from openclaw)
│ └── lib/
│ ├── ... (existing files)
│ ├── brave_search.py # NEW from openclaw
│ ├── parallel_search.py # NEW from openclaw
│ ├── openrouter_search.py # NEW from openclaw
│ ├── youtube_yt.py # Existing
│ └── vendor/bird/ # Existing
└── README.md # Updated with open variant docs
Installation for open variant users
# Claude Code (main skill — unchanged)
git clone https://github.com/mvanhorn/last30days-skill.git ~/.claude/skills/last30days
# Open variant (with watchlist, briefings, history)
git clone https://github.com/mvanhorn/last30days-skill.git ~/.claude/skills/last30days
# Then in Claude Code settings, point skill to variants/open/SKILL.md
# OR symlink:
ln -sf ~/.claude/skills/last30days/variants/open/SKILL.md ~/.claude/skills/last30days-open/SKILL.md
env.py merge strategy
Main's env.py is the base. Add from OpenClaw:
- Three new key names:
PARALLEL_API_KEY,BRAVE_API_KEY,OPENROUTER_API_KEY has_web_search_keys()functionget_web_search_source()function — returns'parallel','brave', or'openrouter'get_available_sources()update to include web-search-capable modes
last30days.py merge strategy
Main's last30days.py is the base. Add from OpenClaw:
_search_web()function that calls the appropriate web search backend--storeCLI flag to persist findings to SQLite--diagnoseCLI flag for source availability diagnostics- Web results integration into the existing report pipeline (normalize → score → dedupe → render)
Keep main's:
- YouTube integration
- Phase 2 supplemental search
- 3-tier Reddit fallback
- Better error handling
- Minimum result guarantee
Portable path resolution (already done)
The main SKILL.md already has portable path resolution (from Codex compat work):
for dir in "." "${CLAUDE_PLUGIN_ROOT:-}" "$HOME/.claude/skills/last30days" ...
The open variant's SKILL.md uses {baseDir} which resolves to the skill root. Both approaches work — we just need to make sure the open variant's references use {baseDir} consistently.
Acceptance Criteria
scripts/store.pyported and working (SQLite creates on first use)scripts/watchlist.pyported with generic scheduling (no OpenClaw cron dependency)scripts/briefing.pyported and generates daily/weekly briefingsscripts/lib/brave_search.pyported and functionalscripts/lib/parallel_search.pyported and functionalscripts/lib/openrouter_search.pyported and functionalscripts/lib/env.pyupdated with web search key supportscripts/last30days.pyhas native_search_web()+--store+--diagnosevariants/open/SKILL.mdexists with command routing (watch, briefing, history, research)variants/open/references/*.md— all 4 reference files ported- Open variant mentions YouTube in description and research output format
- Open variant uses same portable path resolution as main
- Main SKILL.md behavior is unchanged (zero regressions)
python3 scripts/last30days.py "test topic" --mock --emit=compactstill workspython3 scripts/last30days.py "test topic" --diagnoseshows source availability- README documents open variant installation and usage
Dependencies & Risks
| Risk | Likelihood | Mitigation |
|---|---|---|
| OpenClaw's store.py has import dependencies we don't have | Low | store.py uses only stdlib (sqlite3, json, datetime). Self-contained. |
| Web search backends need API keys to test | Medium | Each has a --mock or dry-run path. Test with real keys if available, mock otherwise. |
| watchlist.py depends on OpenClaw cron API | High | Known — strip cron_setup.py dependency, replace with generic docs for launchd/systemd/crontab. |
| Open variant SKILL.md is too long (>500 lines) | Medium | Use reference file pattern (already planned). Router SKILL.md stays under 100 lines. |
| env.py merge introduces regressions | Low | Main's env.py is well-tested. Additive changes only — new keys, new functions. |
| Two SKILL.md files = maintenance burden | Low | They serve different purposes. Main is simple one-shot. Open adds routing. Core engine is shared. |
Files to Create/Modify
New Files
variants/open/SKILL.md— Open variant router (~100 lines)variants/open/references/research.md— One-shot research instructions (from openclaw, updated with YouTube)variants/open/references/watchlist.md— Watchlist management instructions (from openclaw)variants/open/references/briefing.md— Briefing mode instructions (from openclaw)variants/open/references/history.md— History query instructions (from openclaw)variants/open/context.md— Agent memory templatescripts/store.py— SQLite accumulator (from openclaw, as-is)scripts/watchlist.py— Watchlist CLI (from openclaw, genericized)scripts/briefing.py— Briefing generator (from openclaw, as-is)scripts/lib/brave_search.py— Brave Search API (from openclaw)scripts/lib/parallel_search.py— Parallel AI search (from openclaw)scripts/lib/openrouter_search.py— OpenRouter/Sonar Pro search (from openclaw)
Modified Files
scripts/lib/env.py— Add web search key support (~30 lines added)scripts/last30days.py— Add_search_web(),--store,--diagnose(~80 lines added)README.md— Add open variant section (~20 lines)
Total scope: ~12 new files (mostly copied), ~130 lines of new code in existing files.
References
Internal
- OpenClaw plan:
/Users/mvanhorn/last30days-openclaw/docs/plans/2026-02-10-feat-openclaw-last30days-skill-plan.md(989 lines, comprehensive spec) - Codex compat plan:
docs/plans/2026-02-14-feat-codex-skill-compatibility-plan.md(portable paths, platform-neutral text) - OpenClaw source:
/Users/mvanhorn/last30days-openclaw/
Key files to port
store.py:/Users/mvanhorn/last30days-openclaw/scripts/store.py(20KB, SQLite with FTS5)watchlist.py:/Users/mvanhorn/last30days-openclaw/scripts/watchlist.py(10KB)briefing.py:/Users/mvanhorn/last30days-openclaw/scripts/briefing.py(8KB)brave_search.py:/Users/mvanhorn/last30days-openclaw/scripts/lib/brave_search.py(6KB)parallel_search.py:/Users/mvanhorn/last30days-openclaw/scripts/lib/parallel_search.py(4KB)openrouter_search.py:/Users/mvanhorn/last30days-openclaw/scripts/lib/openrouter_search.py(7KB)env.py(openclaw version):/Users/mvanhorn/last30days-openclaw/scripts/lib/env.py(9KB — has web search key functions)