Merge community PRs: Windows Unicode fix, 403 fallback, --days flag
- PR #17 (JosephOIbrahim): Fix UnicodeEncodeError on Windows cp1252 - PR #16 (levineam): Handle HTTP 403 model access errors, add gpt-4.1 fallback - PR #18 (jonthebeef): Add --days=N flag for configurable lookback (1-30) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: last30days
|
name: last30days
|
||||||
description: Research a topic from the last 30 days on Reddit + X + Web, become an expert, and write copy-paste-ready prompts for the user's target tool.
|
description: Research a topic from the last 30 days on Reddit + X + Web, become an expert, and write copy-paste-ready prompts for the user's target tool.
|
||||||
argument-hint: '"[topic] for [tool]" or "[topic]"'
|
argument-hint: '"[topic]" or "[topic] --days=7"'
|
||||||
allowed-tools: Bash, Read, Write, AskUserQuestion, WebSearch
|
allowed-tools: Bash, Read, Write, AskUserQuestion, WebSearch
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -103,7 +103,8 @@ For ALL query types:
|
|||||||
- INCLUDE: blogs, tutorials, docs, news, GitHub repos
|
- INCLUDE: blogs, tutorials, docs, news, GitHub repos
|
||||||
- **DO NOT output "Sources:" list** - this is noise, we'll show stats at the end
|
- **DO NOT output "Sources:" list** - this is noise, we'll show stats at the end
|
||||||
|
|
||||||
**Depth options** (passed through from user's command):
|
**Options** (passed through from user's command):
|
||||||
|
- `--days=N` → Look back N days instead of 30 (e.g., `--days=7` for weekly roundup)
|
||||||
- `--quick` → Faster, fewer sources (8-12 each)
|
- `--quick` → Faster, fewer sources (8-12 each)
|
||||||
- (default) → Balanced (20-30 each)
|
- (default) → Balanced (20-30 each)
|
||||||
- `--deep` → Comprehensive (50-70 Reddit, 40-60 X)
|
- `--deep` → Comprehensive (50-70 Reddit, 40-60 X)
|
||||||
|
|||||||
+18
-4
@@ -323,8 +323,13 @@ def run_research(
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
# Fix Unicode output on Windows (cp1252 can't encode emoji)
|
||||||
|
if sys.platform == "win32":
|
||||||
|
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
||||||
|
sys.stderr.reconfigure(encoding="utf-8", errors="replace")
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Research a topic from the last 30 days on Reddit + X"
|
description="Research a topic from the last N days on Reddit + X"
|
||||||
)
|
)
|
||||||
parser.add_argument("topic", nargs="?", help="Topic to research")
|
parser.add_argument("topic", nargs="?", help="Topic to research")
|
||||||
parser.add_argument("--mock", action="store_true", help="Use fixtures")
|
parser.add_argument("--mock", action="store_true", help="Use fixtures")
|
||||||
@@ -360,6 +365,14 @@ def main():
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="Include general web search alongside Reddit/X (lower weighted)",
|
help="Include general web search alongside Reddit/X (lower weighted)",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--days",
|
||||||
|
type=int,
|
||||||
|
default=30,
|
||||||
|
choices=range(1, 31),
|
||||||
|
metavar="N",
|
||||||
|
help="Number of days to look back (1-30, default: 30)",
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -425,7 +438,7 @@ def main():
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Get date range
|
# Get date range
|
||||||
from_date, to_date = dates.get_date_range(30)
|
from_date, to_date = dates.get_date_range(args.days)
|
||||||
|
|
||||||
# Check what keys are missing for promo messaging
|
# Check what keys are missing for promo messaging
|
||||||
missing_keys = env.get_missing_keys(config)
|
missing_keys = env.get_missing_keys(config)
|
||||||
@@ -543,7 +556,7 @@ def main():
|
|||||||
progress.show_complete(len(deduped_reddit), len(deduped_x))
|
progress.show_complete(len(deduped_reddit), len(deduped_x))
|
||||||
|
|
||||||
# Output result
|
# Output result
|
||||||
output_result(report, args.emit, web_needed, args.topic, from_date, to_date, missing_keys)
|
output_result(report, args.emit, web_needed, args.topic, from_date, to_date, missing_keys, args.days)
|
||||||
|
|
||||||
|
|
||||||
def output_result(
|
def output_result(
|
||||||
@@ -554,6 +567,7 @@ def output_result(
|
|||||||
from_date: str = "",
|
from_date: str = "",
|
||||||
to_date: str = "",
|
to_date: str = "",
|
||||||
missing_keys: str = "none",
|
missing_keys: str = "none",
|
||||||
|
days: int = 30,
|
||||||
):
|
):
|
||||||
"""Output the result based on emit mode."""
|
"""Output the result based on emit mode."""
|
||||||
if emit_mode == "compact":
|
if emit_mode == "compact":
|
||||||
@@ -577,7 +591,7 @@ def output_result(
|
|||||||
print("")
|
print("")
|
||||||
print("Claude: Use your WebSearch tool to find 8-15 relevant web pages.")
|
print("Claude: Use your WebSearch tool to find 8-15 relevant web pages.")
|
||||||
print("EXCLUDE: reddit.com, x.com, twitter.com (already covered above)")
|
print("EXCLUDE: reddit.com, x.com, twitter.com (already covered above)")
|
||||||
print("INCLUDE: blogs, docs, news, tutorials from the last 30 days")
|
print(f"INCLUDE: blogs, docs, news, tutorials from the last {days} days")
|
||||||
print("")
|
print("")
|
||||||
print("After searching, synthesize WebSearch results WITH the Reddit/X")
|
print("After searching, synthesize WebSearch results WITH the Reddit/X")
|
||||||
print("results above. WebSearch items should rank LOWER than comparable")
|
print("results above. WebSearch items should rank LOWER than comparable")
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from . import cache, http
|
|||||||
|
|
||||||
# OpenAI API
|
# OpenAI API
|
||||||
OPENAI_MODELS_URL = "https://api.openai.com/v1/models"
|
OPENAI_MODELS_URL = "https://api.openai.com/v1/models"
|
||||||
OPENAI_FALLBACK_MODELS = ["gpt-5.2", "gpt-5.1", "gpt-5", "gpt-4o"]
|
OPENAI_FALLBACK_MODELS = ["gpt-5.2", "gpt-5.1", "gpt-5", "gpt-4.1", "gpt-4o"]
|
||||||
|
|
||||||
# xAI API - Agent Tools API requires grok-4 family
|
# xAI API - Agent Tools API requires grok-4 family
|
||||||
XAI_MODELS_URL = "https://api.x.ai/v1/models"
|
XAI_MODELS_URL = "https://api.x.ai/v1/models"
|
||||||
@@ -35,8 +35,8 @@ def is_mainline_openai_model(model_id: str) -> bool:
|
|||||||
"""Check if model is a mainline GPT model (not mini/nano/chat/codex/pro)."""
|
"""Check if model is a mainline GPT model (not mini/nano/chat/codex/pro)."""
|
||||||
model_lower = model_id.lower()
|
model_lower = model_id.lower()
|
||||||
|
|
||||||
# Must be gpt-5 series
|
# Must be gpt-4o, gpt-4.1+, or gpt-5+ series (mainline, not mini/nano/etc)
|
||||||
if not re.match(r'^gpt-5(\.\d+)*$', model_lower):
|
if not re.match(r'^gpt-(?:4o|4\.1|5)(\.\d+)*$', model_lower):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Exclude variants
|
# Exclude variants
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ from typing import Any, Dict, List, Optional
|
|||||||
from . import http
|
from . import http
|
||||||
|
|
||||||
# Fallback models when the selected model isn't accessible (e.g., org not verified for GPT-5)
|
# Fallback models when the selected model isn't accessible (e.g., org not verified for GPT-5)
|
||||||
MODEL_FALLBACK_ORDER = ["gpt-4o", "gpt-4o-mini"]
|
MODEL_FALLBACK_ORDER = ["gpt-4.1", "gpt-4o", "gpt-4o-mini"]
|
||||||
|
|
||||||
|
|
||||||
def _log_error(msg: str):
|
def _log_error(msg: str):
|
||||||
@@ -25,7 +25,7 @@ def _log_info(msg: str):
|
|||||||
|
|
||||||
def _is_model_access_error(error: http.HTTPError) -> bool:
|
def _is_model_access_error(error: http.HTTPError) -> bool:
|
||||||
"""Check if error is due to model access/verification issues."""
|
"""Check if error is due to model access/verification issues."""
|
||||||
if error.status_code != 400:
|
if error.status_code not in (400, 403):
|
||||||
return False
|
return False
|
||||||
if not error.body:
|
if not error.body:
|
||||||
return False
|
return False
|
||||||
|
|||||||
Reference in New Issue
Block a user