fix: address plugin layout review feedback

This commit is contained in:
Claire Novotny
2026-04-24 11:52:48 -04:00
parent 72495c1c14
commit f03cb866aa
25 changed files with 54 additions and 27 deletions
+4 -3
View File
@@ -1,16 +1,17 @@
{
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
"name": "last30days-skill",
"description": "Research any topic across Reddit, X, YouTube, TikTok, Instagram, HN, Polymarket, GitHub, and 5+ more sources.",
"owner": {
"name": "Matt Van Horn",
"url": "https://github.com/mvanhorn"
},
"metadata": {
"description": "Marketplace hosting the Last 30 Days research plugin."
},
"plugins": [
{
"name": "last30days",
"description": "Research any topic across Reddit, X, YouTube, TikTok, Instagram, HN, Polymarket, GitHub, and 5+ more sources.",
"version": "3.0.10",
"version": "3.1.1",
"author": {
"name": "Matt Van Horn",
"url": "https://github.com/mvanhorn"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "last30days",
"version": "3.1.0",
"version": "3.1.1",
"description": "Research any topic across Reddit, X, YouTube, TikTok, Instagram, Hacker News, Polymarket, GitHub, and 5+ more sources. AI agent scores by upvotes, likes, and real money - not editors.",
"author": {
"name": "Matt Van Horn",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "last30days",
"version": "3.0.10",
"version": "3.1.1",
"description": "Research any topic across Reddit, X, YouTube, TikTok, Instagram, Hacker News, Polymarket, GitHub, and more sources.",
"author": {
"name": "Matt Van Horn",
+7
View File
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.1.1] - 2026-04-24
### Fixed
- **Codex plugin layout.** Move the canonical runtime payload under `skills/last30days/` and update Codex/Claude plugin metadata and tests for the relocated engine path.
- **Claude Code cache resolution.** Resolve Claude plugin installs to `skills/last30days/scripts/last30days.py` after the plugin-layout restructure.
## [3.1.0] - 2026-04-22
Consolidates the 3.0.10 to 3.0.14 dev cycle (commenter handles, `--competitors`, per-entity Step 0.55, vs-mode N passes, comparison title attribution) and republishes the OpenClaw bundle, which had been frozen on ClawHub at `3.0.0-open` since April 8.
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "last30days-skill"
version = "3.0.10"
version = "3.1.1"
description = "Multi-source last-30-days research skill"
readme = "README.md"
requires-python = ">=3.12"
+10 -4
View File
@@ -1,6 +1,6 @@
---
name: last30days
version: "3.0.10"
version: "3.1.1"
description: "Research what people actually say about any topic in the last 30 days. Pulls posts and engagement from Reddit, X, YouTube, TikTok, Hacker News, Polymarket, GitHub, and the web."
argument-hint: 'last30days nvidia earnings reaction | last30days AI video tools | last30days what users want in react'
allowed-tools: Bash, Read, Write, AskUserQuestion, WebSearch
@@ -233,7 +233,7 @@ If your Bash call to `last30days.py` does NOT include the FULL pre-flight checkl
---
# last30days v3.0.10: Research Any Topic from the Last 30 Days
# last30days v3.1.1: Research Any Topic from the Last 30 Days
> **Permissions overview:** Reads public web/platform data and optionally saves research briefings to `LAST30DAYS_MEMORY_DIR` (defaults to `~/Documents/Last30Days`). X/Twitter search uses optional user-provided tokens (AUTH_TOKEN/CT0 env vars). Bluesky search uses optional app password (BSKY_HANDLE/BSKY_APP_PASSWORD env vars - create at bsky.app/settings/app-passwords). All credential usage and data writes are documented in the [Security & Permissions](#security--permissions) section.
@@ -877,7 +877,13 @@ SKILL_ROOT="${SKILL_ROOT%/}"
if [ -z "$SKILL_ROOT" ] || [ ! -f "$SKILL_ROOT/scripts/last30days.py" ]; then
CLAUDE_PLUGIN_ROOT="$(ls -d "$HOME/.claude/plugins/cache/last30days-skill/last30days/"*/ 2>/dev/null | sort -V | tail -1)"
CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT%/}"
[ -n "$CLAUDE_PLUGIN_ROOT" ] && [ -f "$CLAUDE_PLUGIN_ROOT/scripts/last30days.py" ] && SKILL_ROOT="$CLAUDE_PLUGIN_ROOT"
if [ -n "$CLAUDE_PLUGIN_ROOT" ]; then
if [ -f "$CLAUDE_PLUGIN_ROOT/skills/last30days/scripts/last30days.py" ]; then
SKILL_ROOT="$CLAUDE_PLUGIN_ROOT/skills/last30days"
elif [ -f "$CLAUDE_PLUGIN_ROOT/scripts/last30days.py" ]; then
SKILL_ROOT="$CLAUDE_PLUGIN_ROOT"
fi
fi
fi
# Fallback for repo checkout / Gemini / local development hosts where the plugin cache does not exist.
@@ -890,7 +896,7 @@ fi
if [ -z "${SKILL_ROOT:-}" ] || [ ! -f "$SKILL_ROOT/scripts/last30days.py" ]; then
echo "ERROR: Could not find scripts/last30days.py in Codex/Claude plugin cache or repo checkout" >&2
echo "Expected Codex: $HOME/.codex/plugins/cache/{MARKETPLACE}/last30days/{VERSION}/skills/last30days/scripts/last30days.py" >&2
echo "Expected Claude: $HOME/.claude/plugins/cache/last30days-skill/last30days/{VERSION}/scripts/last30days.py" >&2
echo "Expected Claude: $HOME/.claude/plugins/cache/last30days-skill/last30days/{VERSION}/skills/last30days/scripts/last30days.py" >&2
exit 1
fi
+1 -1
View File
@@ -11,7 +11,7 @@ COMMON_TARGETS=(
# but local development needs the cache kept in sync with the repo.
# Do NOT add ~/.claude/skills/last30days - it creates a duplicate
# /last30days-3 in the slash command menu alongside the plugin version.
"$HOME/.claude/plugins/cache/last30days-skill-private/last30days-3/3.0.10"
"$HOME/.claude/plugins/cache/last30days-skill-private/last30days-3/3.1.1"
"$HOME/.claude/plugins/cache/last30days-skill-private/last30days-3-nogem/3.0.0-nogem"
"$HOME/.agents/skills/last30days"
"$HOME/.codex/skills/last30days"
+3
View File
@@ -4,12 +4,15 @@ import hashlib
import os
import sqlite3
import subprocess
import sys
import tempfile
from pathlib import Path
from unittest import mock
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "skills" / "last30days"))
from scripts.lib.chrome_cookies import (
CHROME_COOKIES_DB,
CHROME_IV_HEX,
+1 -1
View File
@@ -10,7 +10,7 @@ from contextlib import redirect_stderr
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))
sys.path.insert(0, str(REPO_ROOT / "skills" / "last30days" / "scripts"))
import last30days as cli
+1 -1
View File
@@ -13,7 +13,7 @@ from pathlib import Path
from unittest import mock
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))
sys.path.insert(0, str(REPO_ROOT / "skills" / "last30days" / "scripts"))
from lib import fanout
+1 -1
View File
@@ -17,7 +17,7 @@ from pathlib import Path
from unittest import mock
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))
sys.path.insert(0, str(REPO_ROOT / "skills" / "last30days" / "scripts"))
def _fake_report(topic: str):
+1 -1
View File
@@ -11,7 +11,7 @@ from pathlib import Path
from unittest import mock
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))
sys.path.insert(0, str(REPO_ROOT / "skills" / "last30days" / "scripts"))
from lib import competitors
+1 -1
View File
@@ -13,7 +13,7 @@ from pathlib import Path
from unittest import mock
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))
sys.path.insert(0, str(REPO_ROOT / "skills" / "last30days" / "scripts"))
import last30days as cli
@@ -11,7 +11,7 @@ from pathlib import Path
from unittest import mock
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))
sys.path.insert(0, str(REPO_ROOT / "skills" / "last30days" / "scripts"))
def _fake_report(topic: str):
+3
View File
@@ -2,6 +2,7 @@
import configparser
import sqlite3
import sys
import textwrap
from pathlib import Path
from typing import Dict, List, Optional, Tuple
@@ -9,6 +10,8 @@ from unittest.mock import patch
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "skills" / "last30days"))
from scripts.lib.cookie_extract import (
extract_cookies,
extract_firefox_cookies,
@@ -1,3 +1,8 @@
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "skills" / "last30days"))
from scripts.lib import env
+2 -2
View File
@@ -10,11 +10,11 @@ import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))
sys.path.insert(0, str(REPO_ROOT / "skills" / "last30days" / "scripts"))
def _engine() -> Path:
return REPO_ROOT / "scripts" / "last30days.py"
return REPO_ROOT / "skills" / "last30days" / "scripts" / "last30days.py"
class FooterNudgeSuppressionTests(unittest.TestCase):
+1 -1
View File
@@ -10,7 +10,7 @@ from contextlib import redirect_stderr
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))
sys.path.insert(0, str(REPO_ROOT / "skills" / "last30days" / "scripts"))
from lib import planner
+1 -1
View File
@@ -8,7 +8,7 @@ import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))
sys.path.insert(0, str(REPO_ROOT / "skills" / "last30days" / "scripts"))
from lib import polymarket
+1 -1
View File
@@ -9,7 +9,7 @@ import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))
sys.path.insert(0, str(REPO_ROOT / "skills" / "last30days" / "scripts"))
import last30days as cli
from lib import render, schema
+2
View File
@@ -9,6 +9,8 @@ from unittest.mock import patch
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "skills" / "last30days"))
# Import the internal parser directly for testability (avoids platform check)
from scripts.lib.safari_cookies import (
_parse_binary_cookies,
+2 -2
View File
@@ -15,11 +15,11 @@ import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))
sys.path.insert(0, str(REPO_ROOT / "skills" / "last30days" / "scripts"))
def _engine_path() -> Path:
return REPO_ROOT / "scripts" / "last30days.py"
return REPO_ROOT / "skills" / "last30days" / "scripts" / "last30days.py"
class PerEntitySaveFilesTests(unittest.TestCase):
+1 -1
View File
@@ -15,7 +15,7 @@ from contextlib import redirect_stderr
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "scripts"))
sys.path.insert(0, str(REPO_ROOT / "skills" / "last30days" / "scripts"))
from lib import planner
+1 -1
View File
@@ -6,7 +6,7 @@ import unittest
from pathlib import Path
from unittest import mock
sys.path.insert(0, str(Path(__file__).parent.parent / "scripts"))
sys.path.insert(0, str(Path(__file__).parent.parent / "skills" / "last30days" / "scripts"))
from lib import xurl_x
Generated
+1 -1
View File
@@ -197,7 +197,7 @@ wheels = [
[[package]]
name = "last30days-skill"
version = "3.0.10"
version = "3.1.1"
source = { virtual = "." }
dependencies = [
{ name = "requests" },