luckiest-co 1.0.46 → 1.0.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/skills/ab-testing/CHANGELOG.md +5 -0
- package/skills/ab-testing/SKILL.md +12 -9
- package/skills/ab-testing/evals/evals.json +18 -7
- package/skills/ab-testing/references/sample-size-guide.md +2 -2
- package/skills/ads/CHANGELOG.md +5 -0
- package/skills/ads/SKILL.md +4 -4
- package/skills/ai-seo/CHANGELOG.md +7 -0
- package/skills/ai-seo/SKILL.md +27 -20
- package/skills/analytics/CHANGELOG.md +6 -0
- package/skills/analytics/SKILL.md +4 -3
- package/skills/analytics/evals/evals.json +27 -0
- package/skills/analytics/references/ga4-implementation.md +7 -3
- package/skills/analytics/references/gtm-implementation.md +8 -2
- package/skills/aso/CHANGELOG.md +5 -0
- package/skills/aso/SKILL.md +4 -4
- package/skills/aso/evals/evals.json +15 -2
- package/skills/aso/references/benchmarks.md +4 -2
- package/skills/aso/references/google-play-specs.md +5 -1
- package/skills/brainstorm/SKILL.md +59 -16
- package/skills/brainstorm/builder-check-prompt.md +23 -0
- package/skills/churn-prevention/CHANGELOG.md +5 -0
- package/skills/churn-prevention/SKILL.md +3 -3
- package/skills/churn-prevention/evals/evals.json +14 -0
- package/skills/churn-prevention/references/dunning-playbook.md +6 -1
- package/skills/coder-brainstorming/CHANGELOG.md +10 -0
- package/skills/coder-brainstorming/SKILL.md +62 -20
- package/skills/coder-brainstorming/builder-check-prompt.md +23 -0
- package/skills/coder-brainstorming/scripts/server.cjs +1 -1
- package/skills/coder-debugging/CHANGELOG.md +4 -0
- package/skills/coder-debugging/SKILL.md +5 -5
- package/skills/coder-requesting-code-review/CHANGELOG.md +4 -0
- package/skills/coder-requesting-code-review/SKILL.md +4 -4
- package/skills/coder-subagent-driven-development/CHANGELOG.md +4 -0
- package/skills/coder-subagent-driven-development/SKILL.md +3 -3
- package/skills/coder-subagent-driven-development/scripts/sdd-workspace +3 -1
- package/skills/cold-email/CHANGELOG.md +7 -0
- package/skills/cold-email/SKILL.md +27 -7
- package/skills/cold-email/evals/evals.json +30 -0
- package/skills/cold-email/references/benchmarks.md +15 -11
- package/skills/cold-email/references/deliverability.md +110 -0
- package/skills/cold-email/references/linkedin-outreach.md +91 -0
- package/skills/cold-email/references/multichannel-cadence.md +79 -0
- package/skills/cold-email/references/outbound-plays.md +88 -0
- package/skills/cold-email/references/personalization.md +11 -0
- package/skills/cold-email/references/reply-handling.md +65 -0
- package/skills/competitor-profiling/CHANGELOG.md +5 -0
- package/skills/competitor-profiling/SKILL.md +4 -4
- package/skills/content-strategy/CHANGELOG.md +5 -0
- package/skills/content-strategy/SKILL.md +5 -3
- package/skills/copy-editing/CHANGELOG.md +5 -0
- package/skills/copy-editing/SKILL.md +5 -5
- package/skills/copy-editing/references/content-refresh.md +33 -0
- package/skills/cro/CHANGELOG.md +5 -0
- package/skills/cro/SKILL.md +14 -3
- package/skills/cro/references/form.md +1 -1
- package/skills/directory-submissions/CHANGELOG.md +5 -0
- package/skills/directory-submissions/SKILL.md +13 -4
- package/skills/directory-submissions/evals/evals.json +15 -1
- package/skills/directory-submissions/references/directory-list.md +2 -4
- package/skills/emails/CHANGELOG.md +5 -0
- package/skills/emails/SKILL.md +14 -4
- package/skills/launch/CHANGELOG.md +7 -0
- package/skills/launch/SKILL.md +8 -4
- package/skills/launch/evals/evals.json +29 -0
- package/skills/launch/references/shipped-changes.md +84 -0
- package/skills/launch/references/site-launch-qa.md +55 -0
- package/skills/lead-magnets/CHANGELOG.md +5 -0
- package/skills/lead-magnets/SKILL.md +6 -4
- package/skills/lead-magnets/evals/evals.json +18 -0
- package/skills/lead-magnets/references/content-to-skill-corpus-design.md +89 -0
- package/skills/lead-magnets/references/content-to-skill-release.md +71 -0
- package/skills/lead-magnets/references/content-to-skill.md +108 -0
- package/skills/marketing-plan/CHANGELOG.md +5 -0
- package/skills/marketing-plan/SKILL.md +3 -3
- package/skills/marketing-plan/evals/evals.json +13 -0
- package/skills/marketing-plan/references/budget-planning.md +26 -17
- package/skills/pricing/CHANGELOG.md +5 -0
- package/skills/pricing/SKILL.md +4 -4
- package/skills/pricing/evals/evals.json +12 -0
- package/skills/pricing/references/research-methods.md +8 -4
- package/skills/programmatic-seo/CHANGELOG.md +5 -0
- package/skills/programmatic-seo/SKILL.md +8 -4
- package/skills/programmatic-seo/evals/evals.json +13 -0
- package/skills/programmatic-seo/references/implementation-platforms.md +37 -0
- package/skills/prospecting/CHANGELOG.md +7 -0
- package/skills/prospecting/SKILL.md +27 -13
- package/skills/prospecting/evals/evals.json +41 -0
- package/skills/prospecting/references/account-research.md +59 -0
- package/skills/prospecting/references/b2b-prospecting.md +1 -1
- package/skills/prospecting/references/compliance.md +44 -6
- package/skills/prospecting/references/data-sources.md +13 -15
- package/skills/prospecting/references/enrichment-playbook.md +61 -0
- package/skills/prospecting/references/saas-prospecting.md +1 -1
- package/skills/prospecting/references/signal-plays.md +99 -0
- package/skills/prospecting/references/sourcing-and-enrichment.md +89 -0
- package/skills/referrals/CHANGELOG.md +5 -0
- package/skills/referrals/SKILL.md +10 -4
- package/skills/referrals/evals/evals.json +99 -0
- package/skills/referrals/references/affiliate-operations.md +64 -0
- package/skills/referrals/references/affiliate-programs.md +2 -2
- package/skills/referrals/references/program-examples.md +14 -6
- package/skills/referrals/references/review-platform-rules.md +36 -0
- package/skills/referrals/references/review-program-operations.md +62 -0
- package/skills/referrals/references/review-programs.md +96 -0
- package/skills/revops/CHANGELOG.md +5 -0
- package/skills/revops/SKILL.md +10 -6
- package/skills/revops/evals/evals.json +13 -0
- package/skills/revops/references/automation-playbooks.md +1 -1
- package/skills/revops/references/inbound-routing.md +43 -0
- package/skills/revops/references/lifecycle-definitions.md +31 -0
- package/skills/sales-enablement/CHANGELOG.md +5 -0
- package/skills/sales-enablement/SKILL.md +6 -4
- package/skills/sales-enablement/evals/evals.json +14 -0
- package/skills/sales-enablement/references/cold-call-scripts.md +68 -0
- package/skills/schema/CHANGELOG.md +5 -0
- package/skills/schema/SKILL.md +20 -8
- package/skills/schema/evals/evals.json +40 -0
- package/skills/schema/references/schema-examples.md +9 -15
- package/skills/seo-audit/CHANGELOG.md +5 -0
- package/skills/seo-audit/SKILL.md +10 -16
- package/skills/seo-audit/evals/evals.json +68 -0
- package/skills/seo-audit/references/international-seo.md +9 -11
- package/skills/seo-audit/references/local-seo-pages-and-measurement.md +39 -0
- package/skills/seo-audit/references/local-seo-profile-and-citations.md +41 -0
- package/skills/seo-audit/references/local-seo.md +86 -0
- package/skills/seo-audit/references/rankings-push.md +62 -0
- package/skills/seo-audit/references/title-tags.md +89 -0
- package/skills/signup/CHANGELOG.md +5 -0
- package/skills/signup/SKILL.md +7 -3
- package/skills/site-architecture/CHANGELOG.md +5 -0
- package/skills/site-architecture/SKILL.md +5 -4
- package/skills/site-architecture/evals/evals.json +25 -0
- package/skills/site-architecture/references/platforms-and-migration.md +84 -0
- package/skills/social/CHANGELOG.md +5 -0
- package/skills/social/SKILL.md +4 -4
- package/skills/trends/.skillignore +3 -0
- package/skills/trends/CHANGELOG.md +20 -0
- package/skills/trends/SKILL.md +12 -8
- package/skills/trends/references/agent-mode.md +36 -0
- package/skills/trends/references/category-peers.md +38 -0
- package/skills/trends/references/comparison.md +227 -0
- package/skills/trends/references/competitors.md +29 -0
- package/skills/trends/references/discovery.md +90 -0
- package/skills/trends/references/engine-contract.md +111 -1889
- package/skills/trends/references/followups.md +112 -0
- package/skills/trends/references/grok-bot-x.md +55 -0
- package/skills/trends/references/hiring-signals.md +19 -0
- package/skills/trends/references/library-queue.md +33 -0
- package/skills/trends/references/recommendations.md +86 -0
- package/skills/trends/references/research-runbook.md +781 -0
- package/skills/trends/references/runtime.md +165 -0
- package/skills/trends/references/save-html-brief.md +39 -20
- package/skills/trends/references/setup-wizard.md +253 -0
- package/skills/trends/references/synthesis.md +289 -0
- package/skills/trends/scripts/box_chrome_login.py +209 -0
- package/skills/trends/scripts/briefing.py +9 -17
- package/skills/trends/scripts/last30days.py +3247 -280
- package/skills/trends/scripts/lib/agentcookie.py +196 -0
- package/skills/trends/scripts/lib/amazon.py +877 -0
- package/skills/trends/scripts/lib/arxiv.py +42 -8
- package/skills/trends/scripts/lib/backends.py +769 -0
- package/skills/trends/scripts/lib/bird_x.py +340 -57
- package/skills/trends/scripts/lib/bluesky.py +84 -10
- package/skills/trends/scripts/lib/bounded_get.py +156 -0
- package/skills/trends/scripts/lib/brightdata.py +291 -0
- package/skills/trends/scripts/lib/categories.py +7 -1
- package/skills/trends/scripts/lib/chrome_cdp.py +455 -0
- package/skills/trends/scripts/lib/chrome_cookies.py +132 -39
- package/skills/trends/scripts/lib/cluster.py +31 -54
- package/skills/trends/scripts/lib/competitors.py +10 -1
- package/skills/trends/scripts/lib/cookie_extract.py +221 -44
- package/skills/trends/scripts/lib/cookie_paths.py +27 -0
- package/skills/trends/scripts/lib/corpus.py +439 -0
- package/skills/trends/scripts/lib/devto.py +313 -0
- package/skills/trends/scripts/lib/discovery_handoff.py +988 -0
- package/skills/trends/scripts/lib/doctor.py +2166 -0
- package/skills/trends/scripts/lib/dripstack.py +203 -0
- package/skills/trends/scripts/lib/entity_extract.py +37 -0
- package/skills/trends/scripts/lib/env.py +1092 -138
- package/skills/trends/scripts/lib/fanout.py +11 -1
- package/skills/trends/scripts/lib/feed.py +61 -0
- package/skills/trends/scripts/lib/fetch_memo.py +135 -0
- package/skills/trends/scripts/lib/freshness.py +566 -0
- package/skills/trends/scripts/lib/fusion.py +314 -28
- package/skills/trends/scripts/lib/github.py +438 -24
- package/skills/trends/scripts/lib/grok_x.py +1237 -0
- package/skills/trends/scripts/lib/grounding.py +129 -11
- package/skills/trends/scripts/lib/hackernews.py +61 -4
- package/skills/trends/scripts/lib/health.py +373 -0
- package/skills/trends/scripts/lib/hosted.py +333 -0
- package/skills/trends/scripts/lib/html_publish.py +34 -0
- package/skills/trends/scripts/lib/html_render.py +169 -3
- package/skills/trends/scripts/lib/http.py +1215 -70
- package/skills/trends/scripts/lib/instagram.py +132 -9
- package/skills/trends/scripts/lib/jobs.py +4 -2
- package/skills/trends/scripts/lib/library.py +308 -0
- package/skills/trends/scripts/lib/library_index.py +477 -0
- package/skills/trends/scripts/lib/linkedin.py +271 -24
- package/skills/trends/scripts/lib/log.py +5 -2
- package/skills/trends/scripts/lib/meta_ads.py +982 -0
- package/skills/trends/scripts/lib/normalize.py +382 -28
- package/skills/trends/scripts/lib/parallel_mcp.py +234 -0
- package/skills/trends/scripts/lib/permission_preflight.py +42 -3
- package/skills/trends/scripts/lib/perplexity.py +1080 -468
- package/skills/trends/scripts/lib/pipeline.py +5079 -211
- package/skills/trends/scripts/lib/planner.py +396 -53
- package/skills/trends/scripts/lib/polymarket.py +285 -23
- package/skills/trends/scripts/lib/prescriptions.py +432 -0
- package/skills/trends/scripts/lib/private_files.py +21 -0
- package/skills/trends/scripts/lib/providers.py +231 -10
- package/skills/trends/scripts/lib/quality_nudge.py +122 -37
- package/skills/trends/scripts/lib/reddit.py +177 -12
- package/skills/trends/scripts/lib/reddit_arctic.py +146 -5
- package/skills/trends/scripts/lib/reddit_keyless.py +121 -60
- package/skills/trends/scripts/lib/reddit_listing.py +215 -14
- package/skills/trends/scripts/lib/reddit_public.py +12 -13
- package/skills/trends/scripts/lib/reddit_search.py +304 -0
- package/skills/trends/scripts/lib/reddit_shreddit.py +51 -7
- package/skills/trends/scripts/lib/registers.py +134 -0
- package/skills/trends/scripts/lib/relevance.py +17 -0
- package/skills/trends/scripts/lib/render.py +2161 -271
- package/skills/trends/scripts/lib/rerank.py +623 -55
- package/skills/trends/scripts/lib/resolve.py +50 -1
- package/skills/trends/scripts/lib/safari_cookies.py +42 -15
- package/skills/trends/scripts/lib/schema.py +782 -1
- package/skills/trends/scripts/lib/setup_wizard.py +779 -120
- package/skills/trends/scripts/lib/signals.py +206 -7
- package/skills/trends/scripts/lib/stocktwits.py +374 -0
- package/skills/trends/scripts/lib/subproc.py +307 -53
- package/skills/trends/scripts/lib/techmeme.py +89 -64
- package/skills/trends/scripts/lib/telegram.py +317 -0
- package/skills/trends/scripts/lib/tiktok.py +13 -9
- package/skills/trends/scripts/lib/topic_shape.py +340 -0
- package/skills/trends/scripts/lib/transcribe.py +12 -3
- package/skills/trends/scripts/lib/trustpilot.py +256 -5
- package/skills/trends/scripts/lib/truthsocial.py +9 -1
- package/skills/trends/scripts/lib/ui.py +6 -0
- package/skills/trends/scripts/lib/usage.py +132 -0
- package/skills/trends/scripts/lib/web_search_keyless.py +63 -4
- package/skills/trends/scripts/lib/windows_job.py +138 -0
- package/skills/trends/scripts/lib/x_api.py +824 -0
- package/skills/trends/scripts/lib/x_envelope.py +733 -0
- package/skills/trends/scripts/lib/x_judge.py +292 -0
- package/skills/trends/scripts/lib/xai_x.py +26 -3
- package/skills/trends/scripts/lib/xquik.py +89 -15
- package/skills/trends/scripts/lib/xurl_x.py +284 -91
- package/skills/trends/scripts/lib/youtube_yt.py +570 -119
- package/skills/trends/scripts/setup-keychain.sh +12 -4
- package/skills/trends/scripts/setup-pass.sh +3 -0
- package/skills/trends/scripts/store.py +444 -50
- package/skills/trends/scripts/watchlist.py +38 -11
- package/skills/trends/scripts/x_lane_parity.py +107 -0
- package/skills/video-studio/CHANGELOG.md +8 -0
- package/skills/video-studio/SKILL.md +6 -5
- package/skills/video-studio/references/styles/product-flight.md +45 -0
- package/skills/video-studio/references/styles.md +3 -1
- package/skills/video-studio/scripts/qa.mjs +64 -2
- package/skills/video-studio/scripts/qa.test.mjs +29 -1
- package/skills/video-studio-motion/ATTRIBUTION.md +1 -1
- package/skills/video-studio-motion/CHANGELOG.md +4 -0
- package/skills/video-studio-motion/SKILL.md +8 -5
- package/skills/coder-brainstorming/spec-document-reviewer-prompt.md +0 -49
- package/skills/trends/scripts/lib/reddit_rss.py +0 -224
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
"""Fix-prescription registry: the single remediation vocabulary (KTD 7).
|
|
2
|
+
|
|
3
|
+
Each (source, failure mode) entry carries a cause line, a natural-language
|
|
4
|
+
fix, an exact CLI fix, and an optional CONFIGURATION.md anchor. Two real
|
|
5
|
+
consumers keep the vocabulary honest from day one:
|
|
6
|
+
|
|
7
|
+
- ``lib/quality_nudge.py`` builds its post-research fix text from these
|
|
8
|
+
entries (only the fix strings migrated here; trigger logic is untouched).
|
|
9
|
+
- The doctor aggregator (U4) looks entries up per failed source/backend.
|
|
10
|
+
|
|
11
|
+
Because both surfaces read the same entry, the nudge a user sees after a
|
|
12
|
+
degraded run and the prescription doctor prints for the same failure can
|
|
13
|
+
never drift apart.
|
|
14
|
+
|
|
15
|
+
Composition with the other health layers (reference, don't restate):
|
|
16
|
+
|
|
17
|
+
- U1 (``lib/health.py``) owns the machine-aware package-manager strings
|
|
18
|
+
(brew/pipx/apt/npx install-vs-reinstall, off-PATH PATH edits). Binary-class
|
|
19
|
+
entries here pull their static defaults from U1's tables, and
|
|
20
|
+
``for_dependency_probe`` lets a live probe's machine-specific prescription
|
|
21
|
+
win the CLI form while the registry supplies cause/NL/anchor vocabulary.
|
|
22
|
+
- U2 (``lib/backends.py``) embeds this registry's CLI forms inside its
|
|
23
|
+
chain-failure prescriptions, so a backend finding and a registry lookup
|
|
24
|
+
agree on the command to run.
|
|
25
|
+
|
|
26
|
+
No secrets: CLI forms use obvious ``<placeholder>`` values only.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
from dataclasses import dataclass, replace
|
|
32
|
+
from typing import Dict, Optional, Tuple
|
|
33
|
+
|
|
34
|
+
from . import env, health
|
|
35
|
+
from .x_api import BEARER_COVERAGE_NOTE
|
|
36
|
+
|
|
37
|
+
# Direct engine invocation prefix (scripting fallback; the slash-command UX
|
|
38
|
+
# is "ask the agent to run setup ...", which is the natural-language form).
|
|
39
|
+
ENGINE_CLI = "python3 skills/last30days/scripts/last30days.py"
|
|
40
|
+
SETUP_BROWSER_COOKIES_CLI = f"{ENGINE_CLI} setup --allow-browser-cookies"
|
|
41
|
+
SETUP_GITHUB_CLI = f"{ENGINE_CLI} setup --github"
|
|
42
|
+
|
|
43
|
+
# U1 owns these remediation strings; reference them instead of restating.
|
|
44
|
+
_YTDLP_BREW_INSTALL, _YTDLP_BREW_REINSTALL = health.static_prescription("yt-dlp", "brew")
|
|
45
|
+
_YTDLP_PIPX_REINSTALL = health.static_prescription("yt-dlp", "pipx")[1]
|
|
46
|
+
_DIGG_PP_INSTALL_CLI = health.pp_install_cmd("digg")
|
|
47
|
+
|
|
48
|
+
GENERIC_FIX_NL = "see CONFIGURATION.md for setup options for this source"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True)
|
|
52
|
+
class Prescription:
|
|
53
|
+
"""Remediation for one (source, failure mode).
|
|
54
|
+
|
|
55
|
+
``fix_nl`` is the natural-language form ("ask the agent to run setup
|
|
56
|
+
with browser-cookie consent"); ``fix_cli`` is the exact command.
|
|
57
|
+
``alt_cli`` carries per-platform alternates (Windows/pip) when the
|
|
58
|
+
primary CLI form is macOS/brew. ``anchor`` is a CONFIGURATION.md
|
|
59
|
+
heading anchor ("" when the doc has no dedicated section).
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
source: str
|
|
63
|
+
failure: str
|
|
64
|
+
cause: str
|
|
65
|
+
fix_nl: str
|
|
66
|
+
fix_cli: str
|
|
67
|
+
alt_cli: Tuple[str, ...] = ()
|
|
68
|
+
anchor: str = ""
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _entry(source: str, failure: str, **kwargs) -> Tuple[Tuple[str, str], Prescription]:
|
|
72
|
+
return (source, failure), Prescription(source=source, failure=failure, **kwargs)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
REGISTRY: Dict[Tuple[str, str], Prescription] = dict((
|
|
76
|
+
_entry(
|
|
77
|
+
"x", "cookies_missing",
|
|
78
|
+
cause="X browser cookies (AUTH_TOKEN/CT0) are not configured",
|
|
79
|
+
fix_nl=(
|
|
80
|
+
"log into x.com in your browser and re-run (cookies detected "
|
|
81
|
+
"automatically), or add XAI_API_KEY to your .env (get key at "
|
|
82
|
+
"api.x.ai), or add XQUIK_API_KEY to your .env (get key at xquik.com)"
|
|
83
|
+
),
|
|
84
|
+
fix_cli=SETUP_BROWSER_COOKIES_CLI,
|
|
85
|
+
anchor="api-keys-env",
|
|
86
|
+
),
|
|
87
|
+
_entry(
|
|
88
|
+
"x", "cookies_expired",
|
|
89
|
+
cause="X errored this run: cookies are configured but likely expired or revoked",
|
|
90
|
+
fix_nl="log into x.com in your browser, then re-run",
|
|
91
|
+
fix_cli=SETUP_BROWSER_COOKIES_CLI,
|
|
92
|
+
anchor="api-keys-env",
|
|
93
|
+
),
|
|
94
|
+
_entry(
|
|
95
|
+
"x", "xai_error",
|
|
96
|
+
cause="the xAI X search backend failed this run",
|
|
97
|
+
fix_nl=(
|
|
98
|
+
"check XAI_API_KEY and its chat/model permissions in console.x.ai, "
|
|
99
|
+
"confirm LAST30DAYS_X_MODEL names an available model, then re-run"
|
|
100
|
+
),
|
|
101
|
+
fix_cli="XAI_API_KEY=<your-xai-key> LAST30DAYS_X_MODEL=<available-xai-model>",
|
|
102
|
+
anchor="api-keys-env",
|
|
103
|
+
),
|
|
104
|
+
_entry(
|
|
105
|
+
"x", "xai_payment_required",
|
|
106
|
+
cause="the xAI X search backend has no available credits",
|
|
107
|
+
fix_nl="check xAI billing and credits in console.x.ai, then re-run",
|
|
108
|
+
fix_cli=f'{ENGINE_CLI} "<topic>" --search x',
|
|
109
|
+
anchor="api-keys-env",
|
|
110
|
+
),
|
|
111
|
+
_entry(
|
|
112
|
+
"x", "xai_rate_limited",
|
|
113
|
+
cause="the xAI X search backend hit its rate limit",
|
|
114
|
+
fix_nl="wait for the xAI rate limit to reset, then re-run",
|
|
115
|
+
fix_cli=f'{ENGINE_CLI} "<topic>" --search x',
|
|
116
|
+
anchor="api-keys-env",
|
|
117
|
+
),
|
|
118
|
+
_entry(
|
|
119
|
+
"x", "xai_timeout",
|
|
120
|
+
cause="the xAI X search backend timed out",
|
|
121
|
+
fix_nl="xAI timed out; retry the run or select another configured X backend",
|
|
122
|
+
fix_cli=f'{ENGINE_CLI} "<topic>" --search x',
|
|
123
|
+
anchor="api-keys-env",
|
|
124
|
+
),
|
|
125
|
+
_entry(
|
|
126
|
+
"x", "xai_unavailable",
|
|
127
|
+
cause="the xAI X search backend failed this run",
|
|
128
|
+
fix_nl="retry xAI later or select another configured X backend",
|
|
129
|
+
fix_cli=f'{ENGINE_CLI} "<topic>" --search x',
|
|
130
|
+
anchor="api-keys-env",
|
|
131
|
+
),
|
|
132
|
+
_entry(
|
|
133
|
+
"x", "grok_cli_missing",
|
|
134
|
+
cause="the Grok CLI is not installed, so the keyless X path is unavailable",
|
|
135
|
+
fix_nl=(
|
|
136
|
+
"install the Grok CLI (curl -fsSL https://x.ai/cli/install.sh | bash) "
|
|
137
|
+
"and sign in with `grok login` to search X without any X credential"
|
|
138
|
+
),
|
|
139
|
+
fix_cli="npm install -g @xai-official/grok",
|
|
140
|
+
anchor="api-keys-env",
|
|
141
|
+
),
|
|
142
|
+
_entry(
|
|
143
|
+
"x", "grok_not_authenticated",
|
|
144
|
+
cause="the Grok CLI is installed but not signed in",
|
|
145
|
+
fix_nl="sign in to Grok once; no X account or API key is needed after that",
|
|
146
|
+
fix_cli="grok login",
|
|
147
|
+
anchor="api-keys-env",
|
|
148
|
+
),
|
|
149
|
+
# Official X path (an official-only host per env.x_policy, or an explicit
|
|
150
|
+
# xapi pin). Copy is limited to the host X lanes (Grok Bot's built-in X
|
|
151
|
+
# tools, then the X for Grok Bot connector), X_BEARER_TOKEN, XAI_API_KEY,
|
|
152
|
+
# and X API credits; the bearer path is described as about a week, never
|
|
153
|
+
# as parity with the host lanes. Anchors point at
|
|
154
|
+
# the CONFIGURATION.md Grok Bot subsection (slug grok-bot).
|
|
155
|
+
_entry(
|
|
156
|
+
"x", "bearer_missing",
|
|
157
|
+
cause=(
|
|
158
|
+
"no official X path is configured (Grok Bot X tools, X connector, "
|
|
159
|
+
"X_BEARER_TOKEN, or XAI_API_KEY)"
|
|
160
|
+
),
|
|
161
|
+
fix_nl=(
|
|
162
|
+
"fetch X with Grok Bot's built-in X tools and pass the posts with --x-posts, or add the X for Grok Bot "
|
|
163
|
+
"plugin and connect X in Grok Bot settings (full 30-day coverage), or set "
|
|
164
|
+
f"X_BEARER_TOKEN from the X developer console ({BEARER_COVERAGE_NOTE}), "
|
|
165
|
+
"or set XAI_API_KEY from console.x.ai"
|
|
166
|
+
),
|
|
167
|
+
fix_cli="X_BEARER_TOKEN=<your-x-api-bearer-token>",
|
|
168
|
+
anchor="grok-bot",
|
|
169
|
+
),
|
|
170
|
+
_entry(
|
|
171
|
+
"x", "bearer_invalid",
|
|
172
|
+
cause="the X API rejected X_BEARER_TOKEN (401/403)",
|
|
173
|
+
fix_nl=(
|
|
174
|
+
"set a valid X_BEARER_TOKEN from the X developer console "
|
|
175
|
+
f"({BEARER_COVERAGE_NOTE}), or add the X for Grok Bot plugin and connect X in Grok Bot settings "
|
|
176
|
+
"(full 30-day coverage)"
|
|
177
|
+
),
|
|
178
|
+
fix_cli="X_BEARER_TOKEN=<your-x-api-bearer-token>",
|
|
179
|
+
anchor="grok-bot",
|
|
180
|
+
),
|
|
181
|
+
_entry(
|
|
182
|
+
"x", "payment_required",
|
|
183
|
+
cause="X API credits are exhausted (HTTP 402)",
|
|
184
|
+
fix_nl=(
|
|
185
|
+
"top up X API credits in the X developer console, or connect X in "
|
|
186
|
+
"Grok Bot settings (full 30-day coverage)"
|
|
187
|
+
),
|
|
188
|
+
fix_cli="X_BEARER_TOKEN=<bearer-from-a-project-with-credits>",
|
|
189
|
+
anchor="grok-bot",
|
|
190
|
+
),
|
|
191
|
+
_entry(
|
|
192
|
+
"x", "connector_missing",
|
|
193
|
+
cause=(
|
|
194
|
+
"the host X lane was declared but no host-fetched result was "
|
|
195
|
+
"passed to the engine"
|
|
196
|
+
),
|
|
197
|
+
fix_nl=(
|
|
198
|
+
"fetch X with Grok Bot's built-in X tools (or the X for Grok Bot connector) and pass the "
|
|
199
|
+
"posts with --x-posts, or unset LAST30DAYS_X_HOST_LANE and set X_BEARER_TOKEN from the X "
|
|
200
|
+
f"developer console ({BEARER_COVERAGE_NOTE})"
|
|
201
|
+
),
|
|
202
|
+
fix_cli=f'{ENGINE_CLI} "<topic>" --x-posts <path-to-x-posts.json>',
|
|
203
|
+
anchor="grok-bot",
|
|
204
|
+
),
|
|
205
|
+
_entry(
|
|
206
|
+
"scrapecreators", "key_missing",
|
|
207
|
+
cause="SCRAPECREATORS_API_KEY is not set",
|
|
208
|
+
fix_nl=(
|
|
209
|
+
"ask the agent to run setup with the GitHub device flow "
|
|
210
|
+
"(free 10,000-call signup; the key is persisted automatically)"
|
|
211
|
+
),
|
|
212
|
+
fix_cli=SETUP_GITHUB_CLI,
|
|
213
|
+
anchor="api-keys-env",
|
|
214
|
+
),
|
|
215
|
+
_entry(
|
|
216
|
+
"bluesky", "app_password_missing",
|
|
217
|
+
cause="BSKY_HANDLE and/or BSKY_APP_PASSWORD are not set",
|
|
218
|
+
fix_nl=(
|
|
219
|
+
"generate an app password at bsky.app/settings/app-passwords and "
|
|
220
|
+
"add BSKY_HANDLE plus BSKY_APP_PASSWORD to ~/.config/last30days/.env"
|
|
221
|
+
),
|
|
222
|
+
fix_cli="BSKY_HANDLE=<your-handle> BSKY_APP_PASSWORD=<xxxx-xxxx-xxxx-xxxx>",
|
|
223
|
+
anchor="bluesky-app-password-format-and-search-host",
|
|
224
|
+
),
|
|
225
|
+
_entry(
|
|
226
|
+
"youtube", "transcription_key_missing",
|
|
227
|
+
cause=(
|
|
228
|
+
"no transcription provider key for the caption-free transcript "
|
|
229
|
+
"backstop (GROQ_API_KEY or OPENAI_API_KEY)"
|
|
230
|
+
),
|
|
231
|
+
fix_nl=(
|
|
232
|
+
"add a free Groq key from console.groq.com to "
|
|
233
|
+
"~/.config/last30days/.env so caption-free videos still get "
|
|
234
|
+
"transcripts (OPENAI_API_KEY also works as the paid backstop)"
|
|
235
|
+
),
|
|
236
|
+
fix_cli="GROQ_API_KEY=<your-groq-key>",
|
|
237
|
+
anchor="api-keys-env",
|
|
238
|
+
),
|
|
239
|
+
_entry(
|
|
240
|
+
"digg", "pp_cli_missing",
|
|
241
|
+
cause="digg-pp-cli is not installed",
|
|
242
|
+
fix_nl=(
|
|
243
|
+
"install the Digg CLI through the Printing Press library, then "
|
|
244
|
+
"re-run setup so the source activates"
|
|
245
|
+
),
|
|
246
|
+
fix_cli=_DIGG_PP_INSTALL_CLI,
|
|
247
|
+
anchor="first-run-onboarding",
|
|
248
|
+
),
|
|
249
|
+
_entry(
|
|
250
|
+
"digg", "pp_cli_broken",
|
|
251
|
+
cause=(
|
|
252
|
+
"digg-pp-cli resolves on PATH but won't execute (broken or "
|
|
253
|
+
"hanging binary left behind by a bad install)"
|
|
254
|
+
),
|
|
255
|
+
fix_nl=(
|
|
256
|
+
"reinstall the Digg CLI (re-run the Printing Press install) so "
|
|
257
|
+
"the binary actually executes; it is installed but not serving"
|
|
258
|
+
),
|
|
259
|
+
fix_cli=_DIGG_PP_INSTALL_CLI,
|
|
260
|
+
anchor="first-run-onboarding",
|
|
261
|
+
),
|
|
262
|
+
_entry(
|
|
263
|
+
"digg", "pp_cli_off_path",
|
|
264
|
+
cause=(
|
|
265
|
+
"digg-pp-cli is installed but its directory is not on the "
|
|
266
|
+
"agent-subprocess PATH"
|
|
267
|
+
),
|
|
268
|
+
fix_nl=(
|
|
269
|
+
"add the install directory (default ~/.local/bin) to the PATH the "
|
|
270
|
+
"agent subprocess uses; the engine gate only activates the source "
|
|
271
|
+
"when the binary resolves on PATH"
|
|
272
|
+
),
|
|
273
|
+
fix_cli='export PATH="$HOME/.local/bin:$PATH"',
|
|
274
|
+
anchor="first-run-onboarding",
|
|
275
|
+
),
|
|
276
|
+
_entry(
|
|
277
|
+
"youtube", "ytdlp_missing",
|
|
278
|
+
cause="yt-dlp is not installed on the agent-subprocess PATH",
|
|
279
|
+
fix_nl="install yt-dlp to enable the free local YouTube lane",
|
|
280
|
+
fix_cli=_YTDLP_BREW_INSTALL,
|
|
281
|
+
alt_cli=("scoop install yt-dlp", "pip install -U yt-dlp"),
|
|
282
|
+
),
|
|
283
|
+
_entry(
|
|
284
|
+
"youtube", "ytdlp_stale",
|
|
285
|
+
cause=(
|
|
286
|
+
"yt-dlp is installed but stale: YouTube's caption format changes "
|
|
287
|
+
"frequently and old binaries silently fail every transcript"
|
|
288
|
+
),
|
|
289
|
+
fix_nl="update yt-dlp via your package manager",
|
|
290
|
+
fix_cli="brew upgrade yt-dlp",
|
|
291
|
+
alt_cli=("scoop update yt-dlp", "pip install -U yt-dlp"),
|
|
292
|
+
),
|
|
293
|
+
_entry(
|
|
294
|
+
"youtube", "ytdlp_broken",
|
|
295
|
+
cause=(
|
|
296
|
+
"yt-dlp resolves on PATH but won't execute (the stale-shim class: "
|
|
297
|
+
"a wrapper left behind by an interpreter upgrade)"
|
|
298
|
+
),
|
|
299
|
+
fix_nl=(
|
|
300
|
+
"reinstall yt-dlp so the binary actually executes; a plain "
|
|
301
|
+
"install reads as a no-op because the broken shim is still present"
|
|
302
|
+
),
|
|
303
|
+
fix_cli=_YTDLP_BREW_REINSTALL,
|
|
304
|
+
alt_cli=(_YTDLP_PIPX_REINSTALL,),
|
|
305
|
+
),
|
|
306
|
+
_entry(
|
|
307
|
+
"truthsocial", "token_missing",
|
|
308
|
+
cause="TRUTHSOCIAL_TOKEN is not set",
|
|
309
|
+
fix_nl=(
|
|
310
|
+
"log into truthsocial.com in your browser and let setup read the "
|
|
311
|
+
"session cookie, or copy the bearer token from your browser's dev "
|
|
312
|
+
"tools into ~/.config/last30days/.env"
|
|
313
|
+
),
|
|
314
|
+
fix_cli=SETUP_BROWSER_COOKIES_CLI,
|
|
315
|
+
anchor="api-keys-env",
|
|
316
|
+
),
|
|
317
|
+
_entry(
|
|
318
|
+
"xiaohongshu", "service_unreachable",
|
|
319
|
+
cause=(
|
|
320
|
+
"Xiaohongshu browser-session service is unreachable or not logged "
|
|
321
|
+
"in; last30days auto-probes http://localhost:18060 and "
|
|
322
|
+
"http://host.docker.internal:18060 unless XIAOHONGSHU_API_BASE is set"
|
|
323
|
+
),
|
|
324
|
+
fix_nl=(
|
|
325
|
+
"start a local x-mcp browser plugin or xpzouying/xiaohongshu-mcp "
|
|
326
|
+
"service that can see your logged-in Xiaohongshu browser session; "
|
|
327
|
+
"set XIAOHONGSHU_API_BASE only when it runs on a custom host/port"
|
|
328
|
+
),
|
|
329
|
+
fix_cli="XIAOHONGSHU_API_BASE=http://your-host:18060 # only for a custom host; leave unset to auto-probe localhost and host.docker.internal",
|
|
330
|
+
anchor="api-keys-env",
|
|
331
|
+
),
|
|
332
|
+
))
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def lookup(source: str, failure: str) -> Optional[Prescription]:
|
|
336
|
+
"""Return the registered entry for (source, failure), or None."""
|
|
337
|
+
return REGISTRY.get((source, failure))
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
# Failure names remapped on an official-only host (env.x_policy): every
|
|
341
|
+
# cookie or Grok CLI failure has an official-path counterpart, so no fix
|
|
342
|
+
# line there names cookies, the scraper, or the CLI. Official names
|
|
343
|
+
# pass through unchanged on every host.
|
|
344
|
+
_OFFICIAL_X_FAILURES: Dict[str, str] = {
|
|
345
|
+
"cookies_missing": "bearer_missing",
|
|
346
|
+
"cookies_expired": "bearer_invalid",
|
|
347
|
+
"grok_cli_missing": "bearer_missing",
|
|
348
|
+
"grok_not_authenticated": "bearer_missing",
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def for_x(config: dict, failure: str) -> Prescription:
|
|
353
|
+
"""Policy-aware X prescription.
|
|
354
|
+
|
|
355
|
+
On an official-only host (``env.x_policy(config).hint_namespace ==
|
|
356
|
+
"official"``) a default-namespace failure resolves to its official
|
|
357
|
+
counterpart; elsewhere today's entry is returned unchanged. Never
|
|
358
|
+
raises (falls through to ``get``'s generic fallback).
|
|
359
|
+
"""
|
|
360
|
+
if env.x_policy(config or {}).hint_namespace == "official":
|
|
361
|
+
failure = _OFFICIAL_X_FAILURES.get(failure, failure)
|
|
362
|
+
return get("x", failure)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
def get(source: str, failure: str) -> Prescription:
|
|
366
|
+
"""Return the registered entry, or the generic CONFIGURATION.md fallback.
|
|
367
|
+
|
|
368
|
+
Never raises: an unregistered failure mode still yields an actionable
|
|
369
|
+
(if generic) prescription, so a report renderer cannot crash on a
|
|
370
|
+
failure class the registry has not learned yet.
|
|
371
|
+
"""
|
|
372
|
+
entry = lookup(source, failure)
|
|
373
|
+
if entry is not None:
|
|
374
|
+
return entry
|
|
375
|
+
return Prescription(
|
|
376
|
+
source=source,
|
|
377
|
+
failure=failure,
|
|
378
|
+
cause=f"{source}: {failure.replace('_', ' ')}",
|
|
379
|
+
fix_nl=GENERIC_FIX_NL,
|
|
380
|
+
fix_cli=f"{ENGINE_CLI} setup",
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
# ---------------------------------------------------------------------------
|
|
385
|
+
# Composition with U1 dependency probes
|
|
386
|
+
# ---------------------------------------------------------------------------
|
|
387
|
+
|
|
388
|
+
def _dependency_failure(probe: health.DependencyProbe) -> Optional[Tuple[str, str]]:
|
|
389
|
+
"""Map a failed dependency probe onto a registered (source, failure)."""
|
|
390
|
+
if probe.name == "yt-dlp":
|
|
391
|
+
if probe.status == health.MISSING:
|
|
392
|
+
return ("youtube", "ytdlp_missing")
|
|
393
|
+
return ("youtube", "ytdlp_broken") # BROKEN and TIMEOUT: reinstall class
|
|
394
|
+
if probe.name == "digg-pp-cli":
|
|
395
|
+
# health reports off-PATH binaries as MISSING with ``off_path=True``;
|
|
396
|
+
# the distinction only picks cause/NL wording — the probe's own
|
|
397
|
+
# prescription wins the CLI form either way.
|
|
398
|
+
if probe.status == health.MISSING:
|
|
399
|
+
if probe.off_path:
|
|
400
|
+
return ("digg", "pp_cli_off_path")
|
|
401
|
+
return ("digg", "pp_cli_missing")
|
|
402
|
+
return ("digg", "pp_cli_broken") # BROKEN and TIMEOUT: reinstall class
|
|
403
|
+
return None
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def for_dependency_probe(probe: health.DependencyProbe) -> Optional[Prescription]:
|
|
407
|
+
"""Prescription for a failed U1 dependency probe (None when OK).
|
|
408
|
+
|
|
409
|
+
U1's machine-aware prescription (the manager that owns the binary on
|
|
410
|
+
THIS machine, or a PATH edit for off-PATH installs) wins the CLI form;
|
|
411
|
+
the registry entry supplies the shared cause/NL/anchor vocabulary.
|
|
412
|
+
Unregistered dependencies wrap the probe so callers still get both
|
|
413
|
+
fix forms without this module restating U1's strings.
|
|
414
|
+
"""
|
|
415
|
+
if probe.ok:
|
|
416
|
+
return None
|
|
417
|
+
key = _dependency_failure(probe)
|
|
418
|
+
entry = REGISTRY.get(key) if key else None
|
|
419
|
+
if entry is None:
|
|
420
|
+
return Prescription(
|
|
421
|
+
source=probe.name,
|
|
422
|
+
failure=probe.status,
|
|
423
|
+
cause=probe.detail or f"{probe.name}: {probe.status}",
|
|
424
|
+
fix_nl=f"repair the {probe.name} install; {GENERIC_FIX_NL}",
|
|
425
|
+
fix_cli=probe.prescription or f"{ENGINE_CLI} setup",
|
|
426
|
+
)
|
|
427
|
+
updates = {}
|
|
428
|
+
if probe.detail:
|
|
429
|
+
updates["cause"] = probe.detail
|
|
430
|
+
if probe.prescription and probe.prescription != entry.fix_cli:
|
|
431
|
+
updates["fix_cli"] = probe.prescription
|
|
432
|
+
return replace(entry, **updates) if updates else entry
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Private local text files."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def write_private_text(path: Path, content: str, *, encoding: str | None = None) -> int:
|
|
8
|
+
"""Write text with owner-only permissions before any bytes on POSIX."""
|
|
9
|
+
if os.name != "posix":
|
|
10
|
+
return path.write_text(content, encoding=encoding)
|
|
11
|
+
|
|
12
|
+
fd = os.open(path, os.O_CREAT | os.O_WRONLY, 0o600)
|
|
13
|
+
try:
|
|
14
|
+
os.fchmod(fd, 0o600)
|
|
15
|
+
stream = os.fdopen(fd, "w", encoding=encoding)
|
|
16
|
+
except Exception:
|
|
17
|
+
os.close(fd)
|
|
18
|
+
raise
|
|
19
|
+
with stream:
|
|
20
|
+
stream.truncate(0)
|
|
21
|
+
return stream.write(content)
|