diff --git a/README.md b/README.md index 5ff77b8..c3bbfbd 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,12 @@ Instagram Reels is now the 8th signal source. TikTok and Instagram both run on S ## Installation +### Gemini CLI +```bash +gemini extensions install https://github.com/mvanhorn/last30days-skill.git +``` + +### Claude Code / Codex ```bash # Clone the repo git clone https://github.com/mvanhorn/last30days-skill.git ~/.claude/skills/last30days diff --git a/SKILL.md b/SKILL.md index 4d142d9..0f6887c 100644 --- a/SKILL.md +++ b/SKILL.md @@ -172,6 +172,9 @@ Generated: {date} | Sources: Reddit, X, YouTube, TikTok, HN, Polymarket, Web for dir in \ "." \ "${CLAUDE_PLUGIN_ROOT:-}" \ + "${GEMINI_EXTENSION_DIR:-}" \ + "$HOME/.gemini/extensions/last30days-skill" \ + "$HOME/.gemini/extensions/last30days" \ "$HOME/.claude/skills/last30days" \ "$HOME/.agents/skills/last30days" \ "$HOME/.codex/skills/last30days"; do diff --git a/docs/plans/2026-03-08-feat-gemini-cli-support-implementation-plan.md b/docs/plans/2026-03-08-feat-gemini-cli-support-implementation-plan.md new file mode 100644 index 0000000..1ddd7db --- /dev/null +++ b/docs/plans/2026-03-08-feat-gemini-cli-support-implementation-plan.md @@ -0,0 +1,180 @@ +--- +title: "feat: Add Gemini CLI extension support" +type: feat +status: active +date: 2026-03-08 +origin: docs/plans/2026-03-08-review-pr-53-gemini-cli-support-plan.md +--- + +# feat: Add Gemini CLI Extension Support + +## Overview + +Add Gemini CLI compatibility to last30days by cherry-picking the good parts from PR #53 (@alexferrari88) and fixing the implementation problems ourselves. Close PR #53 with a thank-you comment crediting the contributor. + +Based on the review at `docs/plans/2026-03-08-review-pr-53-gemini-cli-support-plan.md`, we accept the concept but fix the approach to match our "one SKILL.md for all platforms" convention established during Codex compatibility work. + +## What We Take from PR #53 + +| Component | Source | Changes Needed | +|-----------|--------|----------------| +| `gemini-extension.json` | PR #53 | Fix settings format (array, not object), bump version to 2.9.5 | +| Path resolution (for loop) | PR #53 | Take as-is, add to both SKILL.md and variants/open/SKILL.md | +| README.md install section | PR #53 | Take as-is | + +## What We Skip from PR #53 + +| Component | Reason | +|-----------|--------| +| `skills/last30days/SKILL.md` (693-line copy) | Duplicate maintenance nightmare - use symlink instead | +| "or" tool name scattering in SKILL.md | LLMs translate tool intent natively - clutters prompt | +| Gemini tool names in `allowed-tools` | Gemini ignores `allowed-tools`; risks breaking Claude Code | + +## Implementation Steps + +### 1. Create `gemini-extension.json` (new file) + +Based on PR #53 but with correct settings format (array per Gemini CLI docs) and v2.9.5 version. + +```json +{ + "name": "last30days-skill", + "version": "2.9.5", + "description": "Research a topic from the last 30 days across Reddit, X, YouTube, TikTok, Instagram, Hacker News, Polymarket, and the web.", + "settings": [ + { + "name": "Extension Directory", + "description": "Extension installation directory (auto-set by Gemini CLI)", + "envVar": "GEMINI_EXTENSION_DIR", + "sensitive": false + }, + { + "name": "ScrapeCreators API Key", + "description": "ScrapeCreators API Key for Reddit, TikTok, and Instagram search (required)", + "envVar": "SCRAPECREATORS_API_KEY", + "sensitive": true + }, + { + "name": "OpenAI API Key", + "description": "OpenAI API Key - optional fallback for Reddit discovery", + "envVar": "OPENAI_API_KEY", + "sensitive": true + }, + { + "name": "xAI API Key", + "description": "xAI API Key for X/Twitter search (optional)", + "envVar": "XAI_API_KEY", + "sensitive": true + }, + { + "name": "OpenRouter API Key", + "description": "OpenRouter API Key (optional)", + "envVar": "OPENROUTER_API_KEY", + "sensitive": true + }, + { + "name": "Parallel AI API Key", + "description": "Parallel AI API Key (optional)", + "envVar": "PARALLEL_API_KEY", + "sensitive": true + }, + { + "name": "Brave Search API Key", + "description": "Brave Search API Key (optional)", + "envVar": "BRAVE_API_KEY", + "sensitive": true + }, + { + "name": "Apify API Token", + "description": "Apify API Token (optional legacy)", + "envVar": "APIFY_API_TOKEN", + "sensitive": true + }, + { + "name": "Twitter AUTH_TOKEN", + "description": "Twitter browser AUTH_TOKEN cookie for direct X search (optional)", + "envVar": "AUTH_TOKEN", + "sensitive": true + }, + { + "name": "Twitter CT0", + "description": "Twitter browser CT0 cookie (optional, pair with AUTH_TOKEN)", + "envVar": "CT0", + "sensitive": true + } + ] +} +``` + +### 2. Create `skills/last30days/SKILL.md` as symlink + +```bash +mkdir -p skills/last30days +ln -s ../../SKILL.md skills/last30days/SKILL.md +``` + +Gemini CLI discovers skills from `skills//SKILL.md`. Symlink ensures single source of truth - no drift, no duplicate maintenance. Git tracks symlinks natively on macOS/Linux (Gemini CLI's target platforms). + +### 3. Add Gemini paths to SKILL.md for-loop (line ~172) + +Add these 3 entries after `"${CLAUDE_PLUGIN_ROOT:-}"`: + +```bash +"${GEMINI_EXTENSION_DIR:-}" \ +"$HOME/.gemini/extensions/last30days-skill" \ +"$HOME/.gemini/extensions/last30days" \ +``` + +### 4. Add same Gemini paths to `variants/open/SKILL.md` for-loop + +Same 3 entries, same position. + +### 5. Update `README.md` install section + +Add Gemini CLI install before existing Claude Code section: + +```markdown +### Gemini CLI +\`\`\`bash +gemini extensions install https://github.com/mvanhorn/last30days-skill.git +\`\`\` + +### Claude Code / Codex +``` + +### 6. Close PR #53 with credit + +Post comment thanking @alexferrari88, explaining we incorporated their work with modifications, and close the PR. Credit them in the commit message. + +## Acceptance Criteria + +- [ ] `gemini-extension.json` exists with correct array-format settings +- [ ] `skills/last30days/SKILL.md` is a symlink to `../../SKILL.md` +- [ ] Gemini path entries in SKILL.md for-loop (3 new entries) +- [ ] Gemini path entries in variants/open/SKILL.md for-loop (3 new entries) +- [ ] README.md has Gemini CLI install instructions +- [ ] No changes to `allowed-tools` in any SKILL.md +- [ ] No "or" tool name alternatives in any SKILL.md body +- [ ] Claude Code still works (`/last30days` runs normally) +- [ ] PR #53 closed with credit to @alexferrari88 +- [ ] Commit message credits @alexferrari88 as co-author + +## Confidence Assessment + +**High confidence.** All changes are additive: +- New file (gemini-extension.json) - zero conflict risk +- Symlink (skills/last30days/SKILL.md) - zero conflict risk +- 3 lines added to a for-loop - trivially safe, short-circuits on first match +- README addition - simple text +- No changes to SKILL.md content, allowed-tools, or Python scripts +- No changes that could break existing Claude Code behavior + +The only untested aspect is whether Gemini CLI correctly follows the symlink for skill discovery. If symlinks cause issues, fallback is a 2-line SKILL.md stub that imports the root (but this is unlikely - Gemini CLI runs on macOS/Linux where symlinks are native). + +## Sources + +- PR #53: https://github.com/mvanhorn/last30days-skill/pull/53 +- Issue #45: https://github.com/mvanhorn/last30days-skill/issues/45 +- Gemini CLI extension reference (settings array format confirmed): https://geminicli.com/docs/extensions/reference/ +- Gemini CLI skills docs: https://geminicli.com/docs/cli/creating-skills/ +- Review plan: `docs/plans/2026-03-08-review-pr-53-gemini-cli-support-plan.md` diff --git a/docs/plans/2026-03-08-fix-skill-md-save-section-regression-plan.md b/docs/plans/2026-03-08-fix-skill-md-save-section-regression-plan.md index 0cc0cc5..de80801 100644 --- a/docs/plans/2026-03-08-fix-skill-md-save-section-regression-plan.md +++ b/docs/plans/2026-03-08-fix-skill-md-save-section-regression-plan.md @@ -1,7 +1,7 @@ --- title: "fix: Remove re-introduced Save Research to Documents section from SKILL.md" type: fix -status: active +status: completed date: 2026-03-08 --- diff --git a/docs/plans/2026-03-08-review-pr-53-gemini-cli-support-plan.md b/docs/plans/2026-03-08-review-pr-53-gemini-cli-support-plan.md new file mode 100644 index 0000000..667d201 --- /dev/null +++ b/docs/plans/2026-03-08-review-pr-53-gemini-cli-support-plan.md @@ -0,0 +1,157 @@ +--- +title: "review: PR #53 - Gemini CLI Support" +type: review +status: active +date: 2026-03-08 +--- + +# Review: PR #53 - Gemini CLI Support (alexferrari88) + +## Verdict: MODIFY - Accept concept, reject implementation approach + +PR #53 by @alexferrari88 adds Gemini CLI extension support. The intent is good and closes issue #45, but the implementation has structural problems that would create the exact maintenance nightmare we just fixed in v2.9.5. + +## PR Summary + +| File | Changes | Assessment | +|------|---------|------------| +| `gemini-extension.json` | +67 new file | **Accept with fixes** | +| `skills/last30days/SKILL.md` | +693 new file (full copy) | **Reject** - duplicate SKILL.md | +| `SKILL.md` | +9/-6 (tool name scattering) | **Reject** - wrong approach | +| `variants/open/SKILL.md` | +4/-1 | **Partially accept** (path resolution yes, tool names no) | +| `README.md` | +6 install instructions | **Accept** | + +## Critical Issues + +### 1. Duplicated SKILL.md (BLOCKER) + +The PR creates `skills/last30days/SKILL.md` as a **full 693-line copy** of the root `SKILL.md`. This is the exact problem we just spent hours debugging - PR merges on March 7 regressed v2.9.4's save-section removal because branches had stale copies. A second SKILL.md guarantees this happens again. + +The Codex compatibility work (see `docs/plans/2026-02-14-feat-codex-skill-compatibility-plan.md`) explicitly chose **one SKILL.md for all platforms** to avoid this. Gemini CLI should follow the same pattern. + +**Fix:** Delete `skills/last30days/SKILL.md`. Gemini CLI discovers skills from the extension's `skills/` directory, but we can either: +- (a) Symlink: `skills/last30days/SKILL.md -> ../../SKILL.md` +- (b) Use the root SKILL.md directly and configure `contextFileName` in gemini-extension.json to point to it +- (c) Have `skills/last30days/SKILL.md` be a thin wrapper that says "See root SKILL.md" (least ideal) + +### 2. Based on v2.9.1, not v2.9.5 (BLOCKER) + +The PR's copy of SKILL.md is based on v2.9.1 and includes: +- The "Save Research to Documents" section (removed in v2.9.4, re-removed in v2.9.5) +- Old agent mode line referencing deleted section +- Missing `--save-dir=~/Documents/Last30Days` flag +- Old version number + +**Fix:** Rebase on current main (v2.9.5). + +### 3. "Or" tool name scattering (REJECT) + +The PR adds `WebSearch or google_web_search(...)` and similar patterns throughout SKILL.md. This is the wrong approach because: + +- **LLMs already translate intent to tools.** When Gemini reads "do a WebSearch for X", it knows to use `google_web_search`. When Claude reads it, it uses `WebSearch`. The model handles this mapping natively. +- **Clutters the prompt.** SKILL.md is a 640-line prompt. Adding "or alternative_name" to every tool reference makes it harder for the model to parse. +- **Maintenance burden.** Every new platform means adding more "or" alternatives. + +**Fix:** Remove all "or" alternatives. Keep Claude Code tool names in the SKILL.md body (they work as intent descriptions). If Gemini needs explicit tool mapping, that belongs in `GEMINI.md` (Gemini CLI's context file), not scattered through the skill instructions. + +### 4. `allowed-tools` pollution (RISKY) + +Adding `run_shell_command, read_file, write_file, ask_user, google_web_search` to `allowed-tools`: + +``` +allowed-tools: Bash, Read, Write, AskUserQuestion, WebSearch, run_shell_command, read_file, write_file, ask_user, google_web_search +``` + +**Risk:** If Claude Code's parser is strict and rejects unknown tool names, this breaks the skill for all Claude Code users. If it silently ignores unknown names, it's harmless but noisy. + +**Finding:** Gemini CLI only recognizes `name` and `description` in SKILL.md frontmatter. It **ignores** `allowed-tools` entirely. So adding Gemini tool names to `allowed-tools` provides zero benefit to Gemini users while potentially breaking Claude Code users. + +**Fix:** Remove Gemini tool names from `allowed-tools`. They serve no purpose on either platform. + +## What to Accept + +### 1. gemini-extension.json (with fixes) + +The manifest file is the right approach. However: + +- [ ] **Verify settings format.** The PR uses object-key format (`"SCRAPECREATORS_API_KEY": { ... }`). Gemini CLI docs show array format (`[{ "name": "...", ... }]`). Need to confirm which is correct for the current Gemini CLI version. The researcher found array format in the docs. +- [ ] **Update version** from `2.9.1` to `2.9.5` +- [ ] **Consider adding `contextFileName`** to point to root SKILL.md instead of duplicating + +### 2. Path resolution additions + +Adding these to the bash `for` loop is correct and low-risk: + +```bash +"${GEMINI_EXTENSION_DIR:-}" \ +"$HOME/.gemini/extensions/last30days-skill" \ +"$HOME/.gemini/extensions/last30days" \ +``` + +This should be in both `SKILL.md` and `variants/open/SKILL.md`. + +### 3. README.md install section + +Clean and appropriate. Adding Gemini CLI install command before Claude Code section. + +## Proposed Changes to Request from Contributor + +### Must-fix (before merge) + +1. **Delete `skills/last30days/SKILL.md`** - no duplicate. Either symlink or use `contextFileName` in manifest. +2. **Rebase on main** (v2.9.5) - the PR is based on stale code. +3. **Remove all "or" tool name alternatives** from SKILL.md and variants/open/SKILL.md body text. +4. **Remove Gemini tool names from `allowed-tools`** in all SKILL.md files. +5. **Verify `gemini-extension.json` settings format** against current Gemini CLI docs (array vs object). + +### Nice-to-have + +6. **Add a `GEMINI.md` context file** (optional) - can include a short note like "When this skill references 'WebSearch', use `google_web_search`. When it references 'Bash', use `run_shell_command`." This is the clean way to handle tool name translation. +7. **Update sync.sh** to optionally deploy to `~/.gemini/extensions/last30days/` (debatable - Gemini users may prefer `gemini extensions install` instead). +8. **Add `.gemini/` to the path check in sync.sh** import verification. + +## Testing Plan + +Before merging, verify: + +- [ ] `gemini extensions install` works from the repo (or `gemini extensions link .` for local dev) +- [ ] Skill activates in Gemini CLI and the model can find `scripts/last30days.py` +- [ ] `GEMINI_EXTENSION_DIR` env var resolves correctly in the bash for-loop +- [ ] Claude Code still works with no regressions (run `/last30days test topic --mock` or similar) +- [ ] `allowed-tools` with only Claude Code tool names doesn't break Gemini CLI skill loading + +## Comment Template for PR + +``` +Thanks for the contribution! Gemini CLI support is great to have, and the `gemini-extension.json` manifest and path resolution additions are solid. + +A few things need changing before we can merge: + +**Must-fix:** + +1. **Remove `skills/last30days/SKILL.md`** - We maintain one SKILL.md to avoid sync drift (we literally just fixed a regression from this exact problem yesterday). Either symlink it or use `contextFileName` in the manifest to point to the root SKILL.md. + +2. **Rebase on `main`** - The PR is based on v2.9.1 but we're now at v2.9.5. The "Save Research to Documents" section in your copy was removed, `--save-dir` was added to the bash command, and the version was bumped. + +3. **Remove "or" tool name alternatives** from SKILL.md body (e.g., `WebSearch or google_web_search`). LLMs handle tool name translation natively - Gemini knows to use `google_web_search` when the skill says "search the web". Scattering alternatives clutters the prompt. + +4. **Remove Gemini tool names from `allowed-tools`** - Gemini CLI ignores `allowed-tools` (it only reads `name` and `description` from SKILL.md frontmatter), so these provide no benefit. And they risk breaking Claude Code if its parser rejects unknown tool names. + +5. **Verify `gemini-extension.json` settings format** - The Gemini CLI docs I found show settings as an array (`[{ "name": "...", ... }]`), not object keys (`{ "KEY": { ... } }`). Can you confirm which format your Gemini CLI version expects? + +**Optional but recommended:** + +6. Consider adding a `GEMINI.md` context file with a short tool-name translation note (e.g., "When this skill says 'WebSearch', use `google_web_search`"). This is the clean way to bridge tool names. + +Happy to help work through any of these! The core approach (manifest + path resolution) is right. +``` + +## Sources + +- PR #53: https://github.com/mvanhorn/last30days-skill/pull/53 +- Issue #45: https://github.com/mvanhorn/last30days-skill/issues/45 +- Gemini CLI extension docs: https://geminicli.com/docs/extensions/writing-extensions/ +- Gemini CLI extension reference: https://geminicli.com/docs/extensions/reference/ +- Gemini CLI skills docs: https://geminicli.com/docs/cli/creating-skills/ +- Codex compatibility plan: `docs/plans/2026-02-14-feat-codex-skill-compatibility-plan.md` +- v2.9.5 regression fix: commit `8f7fb5a` (today) diff --git a/gemini-extension.json b/gemini-extension.json new file mode 100644 index 0000000..0bf2760 --- /dev/null +++ b/gemini-extension.json @@ -0,0 +1,67 @@ +{ + "name": "last30days-skill", + "version": "2.9.5", + "description": "Research a topic from the last 30 days across Reddit, X, YouTube, TikTok, Instagram, Hacker News, Polymarket, and the web.", + "settings": [ + { + "name": "Extension Directory", + "description": "Extension installation directory (auto-set by Gemini CLI)", + "envVar": "GEMINI_EXTENSION_DIR", + "sensitive": false + }, + { + "name": "ScrapeCreators API Key", + "description": "ScrapeCreators API Key for Reddit, TikTok, and Instagram search (required)", + "envVar": "SCRAPECREATORS_API_KEY", + "sensitive": true + }, + { + "name": "OpenAI API Key", + "description": "OpenAI API Key - optional fallback for Reddit discovery", + "envVar": "OPENAI_API_KEY", + "sensitive": true + }, + { + "name": "xAI API Key", + "description": "xAI API Key for X/Twitter search (optional)", + "envVar": "XAI_API_KEY", + "sensitive": true + }, + { + "name": "OpenRouter API Key", + "description": "OpenRouter API Key (optional)", + "envVar": "OPENROUTER_API_KEY", + "sensitive": true + }, + { + "name": "Parallel AI API Key", + "description": "Parallel AI API Key (optional)", + "envVar": "PARALLEL_API_KEY", + "sensitive": true + }, + { + "name": "Brave Search API Key", + "description": "Brave Search API Key (optional)", + "envVar": "BRAVE_API_KEY", + "sensitive": true + }, + { + "name": "Apify API Token", + "description": "Apify API Token (optional legacy)", + "envVar": "APIFY_API_TOKEN", + "sensitive": true + }, + { + "name": "Twitter AUTH_TOKEN", + "description": "Twitter browser AUTH_TOKEN cookie for direct X search (optional)", + "envVar": "AUTH_TOKEN", + "sensitive": true + }, + { + "name": "Twitter CT0", + "description": "Twitter browser CT0 cookie (optional, pair with AUTH_TOKEN)", + "envVar": "CT0", + "sensitive": true + } + ] +} diff --git a/skills/last30days/SKILL.md b/skills/last30days/SKILL.md new file mode 120000 index 0000000..4215fae --- /dev/null +++ b/skills/last30days/SKILL.md @@ -0,0 +1 @@ +../../SKILL.md \ No newline at end of file diff --git a/variants/open/SKILL.md b/variants/open/SKILL.md index e36e324..c43381a 100644 --- a/variants/open/SKILL.md +++ b/variants/open/SKILL.md @@ -27,6 +27,9 @@ Parse the user's first argument to determine the mode: for dir in \ "." \ "${CLAUDE_PLUGIN_ROOT:-}" \ + "${GEMINI_EXTENSION_DIR:-}" \ + "$HOME/.gemini/extensions/last30days-skill" \ + "$HOME/.gemini/extensions/last30days" \ "$HOME/.claude/skills/last30days" \ "$HOME/.agents/skills/last30days" \ "$HOME/.codex/skills/last30days"; do