feat(nux): conversational NUX with "last30" trigger and dynamic source status
Replace CLI-style promo banners with casual, conversational NUX message that teaches users to say "last30" + natural language. Remove stale Bird CLI npm install references and dead prompt_bird_install methods. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
---
|
||||
name: last30days
|
||||
version: "2.1"
|
||||
description: Research a topic from the last 30 days on Reddit + X + YouTube + Web, become an expert, and write copy-paste-ready prompts for the user's target tool.
|
||||
argument-hint: 'nano banana pro prompts, NVIDIA news, best AI video tools'
|
||||
description: "Research a topic from the last 30 days. Also triggered by 'last30'. Sources: Reddit, X, YouTube, web. Become an expert and write copy-paste-ready prompts."
|
||||
argument-hint: 'last30 AI video tools, last30 best project management tools'
|
||||
allowed-tools: Bash, Read, Write, AskUserQuestion, WebSearch
|
||||
---
|
||||
|
||||
|
||||
@@ -924,9 +924,9 @@ def main():
|
||||
# Check what keys are missing for promo messaging
|
||||
missing_keys = env.get_missing_keys(config)
|
||||
|
||||
# Show promo for missing keys BEFORE research
|
||||
# Show NUX / promo for missing keys BEFORE research
|
||||
if missing_keys != 'none':
|
||||
progress.show_promo(missing_keys)
|
||||
progress.show_promo(missing_keys, diag=diag)
|
||||
|
||||
# Select models
|
||||
if args.mock:
|
||||
|
||||
+33
-107
@@ -87,84 +87,48 @@ WEB_ONLY_MESSAGES = [
|
||||
"Discovering tutorials...",
|
||||
]
|
||||
|
||||
# Promo message for users without API keys
|
||||
PROMO_MESSAGE = f"""
|
||||
{Colors.YELLOW}{Colors.BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{Colors.RESET}
|
||||
{Colors.YELLOW}⚡ UNLOCK THE FULL POWER OF /last30days{Colors.RESET}
|
||||
def _build_nux_message(diag: dict = None) -> str:
|
||||
"""Build conversational NUX message with dynamic source status."""
|
||||
if diag:
|
||||
reddit = "✓" if diag.get("openai") else "✗"
|
||||
x = "✓" if diag.get("x_source") else "✗"
|
||||
youtube = "✓" if diag.get("youtube") else "✗"
|
||||
web = "✓" if diag.get("web_search_backend") else "✗"
|
||||
status_line = f"Reddit {reddit}, X {x}, YouTube {youtube}, Web {web}"
|
||||
else:
|
||||
status_line = "YouTube ✓, Web ✓, Reddit ✗, X ✗"
|
||||
|
||||
{Colors.DIM}Right now you're using web search only. Unlock more sources:{Colors.RESET}
|
||||
return f"""
|
||||
I just researched that for you. Here's what I've got right now:
|
||||
|
||||
{Colors.YELLOW}🟠 Reddit{Colors.RESET} - Real upvotes, comments, and community insights
|
||||
└─ Add OPENAI_API_KEY (uses OpenAI's web_search for Reddit)
|
||||
{status_line}
|
||||
|
||||
{Colors.CYAN}🔵 X (Twitter){Colors.RESET} - Real-time posts, likes, reposts from creators
|
||||
└─ {Colors.GREEN}FREE:{Colors.RESET} npm install -g @steipete/bird {Colors.DIM}(uses browser session){Colors.RESET}
|
||||
└─ {Colors.DIM}Or:{Colors.RESET} Add XAI_API_KEY (paid API)
|
||||
You can unlock more sources with API keys — just ask me how and I'll walk you through it. More sources means better research, but it works fine as-is.
|
||||
|
||||
{Colors.DIM}Setup:{Colors.RESET} Edit {Colors.BOLD}~/.config/last30days/.env{Colors.RESET}
|
||||
{Colors.YELLOW}{Colors.BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{Colors.RESET}
|
||||
"""
|
||||
Some examples of what you can do:
|
||||
- "last30 what are people saying about Figma"
|
||||
- "last30 watch my biggest competitor every week"
|
||||
- "last30 tell me once a month about what my board members are up to"
|
||||
- "last30 give me my morning briefing"
|
||||
- "last30 what have you found about AI video?"
|
||||
- "last30 set a budget so you don't go crazy"
|
||||
|
||||
PROMO_MESSAGE_PLAIN = """
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
⚡ UNLOCK THE FULL POWER OF /last30days
|
||||
|
||||
Right now you're using web search only. Unlock more sources:
|
||||
|
||||
🟠 Reddit - Real upvotes, comments, and community insights
|
||||
└─ Add OPENAI_API_KEY (uses OpenAI's web_search for Reddit)
|
||||
|
||||
🔵 X (Twitter) - Real-time posts, likes, reposts from creators
|
||||
└─ FREE: npm install -g @steipete/bird (uses browser session)
|
||||
└─ Or: Add XAI_API_KEY (paid API)
|
||||
|
||||
Setup: Edit ~/.config/last30days/.env
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Just start with "last30" and talk to me like normal.
|
||||
"""
|
||||
|
||||
# Shorter promo for single missing key
|
||||
PROMO_SINGLE_KEY = {
|
||||
"reddit": f"""
|
||||
{Colors.DIM}💡 Tip: Add {Colors.YELLOW}OPENAI_API_KEY{Colors.RESET}{Colors.DIM} to ~/.config/last30days/.env for Reddit data with real engagement metrics!{Colors.RESET}
|
||||
""",
|
||||
"x": f"""
|
||||
{Colors.DIM}💡 Tip: For X/Twitter data with real likes & reposts:{Colors.RESET}
|
||||
{Colors.GREEN}FREE:{Colors.RESET} npm install -g @steipete/bird {Colors.DIM}(uses browser session){Colors.RESET}
|
||||
{Colors.DIM}Or: Add XAI_API_KEY to ~/.config/last30days/.env{Colors.RESET}
|
||||
""",
|
||||
"reddit": "\n💡 You can unlock Reddit with an OpenAI API key — just ask me how.\n",
|
||||
"x": "\n💡 You can unlock X with an xAI API key — just ask me how.\n",
|
||||
}
|
||||
|
||||
PROMO_SINGLE_KEY_PLAIN = {
|
||||
"reddit": "\n💡 Tip: Add OPENAI_API_KEY to ~/.config/last30days/.env for Reddit data with real engagement metrics!\n",
|
||||
"x": "\n💡 Tip: For X/Twitter data with real likes & reposts:\n FREE: npm install -g @steipete/bird (uses browser session)\n Or: Add XAI_API_KEY to ~/.config/last30days/.env\n",
|
||||
}
|
||||
|
||||
# Bird CLI prompts
|
||||
BIRD_INSTALL_PROMPT = f"""
|
||||
{Colors.CYAN}{Colors.BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{Colors.RESET}
|
||||
{Colors.CYAN}🐦 FREE X/TWITTER SEARCH AVAILABLE{Colors.RESET}
|
||||
|
||||
Bird CLI provides free X search using your browser session (no API key needed).
|
||||
|
||||
"""
|
||||
|
||||
BIRD_INSTALL_PROMPT_PLAIN = """
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🐦 FREE X/TWITTER SEARCH AVAILABLE
|
||||
|
||||
Bird CLI provides free X search using your browser session (no API key needed).
|
||||
|
||||
"""
|
||||
|
||||
# Bird auth help (for local users with vendored Bird CLI)
|
||||
BIRD_AUTH_HELP = f"""
|
||||
{Colors.YELLOW}Bird authentication failed.{Colors.RESET}
|
||||
|
||||
To fix this:
|
||||
1. Log into X (twitter.com) in Safari, Chrome, or Firefox
|
||||
2. Run: {Colors.BOLD}bird check{Colors.RESET} to verify credentials
|
||||
3. Try again
|
||||
|
||||
For manual setup, see: https://github.com/steipete/bird#authentication
|
||||
2. Try again — Bird reads your browser cookies automatically.
|
||||
"""
|
||||
|
||||
BIRD_AUTH_HELP_PLAIN = """
|
||||
@@ -172,10 +136,7 @@ Bird authentication failed.
|
||||
|
||||
To fix this:
|
||||
1. Log into X (twitter.com) in Safari, Chrome, or Firefox
|
||||
2. Run: bird check to verify credentials
|
||||
3. Try again
|
||||
|
||||
For manual setup, see: https://github.com/steipete/bird#authentication
|
||||
2. Try again — Bird reads your browser cookies automatically.
|
||||
"""
|
||||
|
||||
# Spinner frames
|
||||
@@ -357,52 +318,17 @@ class ProgressDisplay:
|
||||
sys.stderr.write(f"✓ Ready for web search ({elapsed:.1f}s)\n")
|
||||
sys.stderr.flush()
|
||||
|
||||
def show_promo(self, missing: str = "both"):
|
||||
"""Show promotional message for missing API keys.
|
||||
def show_promo(self, missing: str = "both", diag: dict = None):
|
||||
"""Show NUX / promotional message for missing API keys.
|
||||
|
||||
Args:
|
||||
missing: 'both', 'reddit', or 'x' - which keys are missing
|
||||
missing: 'both', 'all', 'reddit', or 'x' - which keys are missing
|
||||
diag: Optional diagnostics dict for dynamic source status
|
||||
"""
|
||||
if missing == "both":
|
||||
if IS_TTY:
|
||||
sys.stderr.write(PROMO_MESSAGE)
|
||||
else:
|
||||
sys.stderr.write(PROMO_MESSAGE_PLAIN)
|
||||
if missing in ("both", "all"):
|
||||
sys.stderr.write(_build_nux_message(diag))
|
||||
elif missing in PROMO_SINGLE_KEY:
|
||||
if IS_TTY:
|
||||
sys.stderr.write(PROMO_SINGLE_KEY[missing])
|
||||
else:
|
||||
sys.stderr.write(PROMO_SINGLE_KEY_PLAIN[missing])
|
||||
sys.stderr.flush()
|
||||
|
||||
def prompt_bird_install(self) -> bool:
|
||||
"""Prompt user to install Bird CLI.
|
||||
|
||||
Returns:
|
||||
True if user wants to install, False otherwise.
|
||||
"""
|
||||
if IS_TTY:
|
||||
sys.stderr.write(BIRD_INSTALL_PROMPT)
|
||||
else:
|
||||
sys.stderr.write(BIRD_INSTALL_PROMPT_PLAIN)
|
||||
sys.stderr.flush()
|
||||
|
||||
try:
|
||||
response = input("Install Bird CLI now? (y/n): ").strip().lower()
|
||||
return response in ('y', 'yes')
|
||||
except (EOFError, KeyboardInterrupt):
|
||||
return False
|
||||
|
||||
def show_bird_install_success(self, username: str):
|
||||
"""Show Bird installation success message."""
|
||||
msg = f"{Colors.GREEN}✓ Bird installed and authenticated as @{username}{Colors.RESET}\n" if IS_TTY else f"✓ Bird installed and authenticated as @{username}\n"
|
||||
sys.stderr.write(msg)
|
||||
sys.stderr.flush()
|
||||
|
||||
def show_bird_install_failed(self, error: str):
|
||||
"""Show Bird installation failure message."""
|
||||
msg = f"{Colors.RED}✗ Bird installation failed: {error}{Colors.RESET}\n" if IS_TTY else f"✗ Bird installation failed: {error}\n"
|
||||
sys.stderr.write(msg)
|
||||
sys.stderr.flush()
|
||||
|
||||
def show_bird_auth_help(self):
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
name: last30days
|
||||
version: "2.1-open"
|
||||
description: "Research topics, manage watchlists, and get morning briefings — all from Reddit, X, YouTube, and the web."
|
||||
argument-hint: 'AI video tools, watch add "NVIDIA news", briefing, history --trending'
|
||||
description: "Research topics, manage watchlists, get briefings, query history. Also triggered by 'last30'. Sources: Reddit, X, YouTube, web."
|
||||
argument-hint: 'last30 AI video tools, last30 watch my competitor every week, last30 give me my briefing'
|
||||
allowed-tools: Bash, Read, Write, AskUserQuestion, WebSearch
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user