Detect buying signals across TAM companies and watchlist personas. Three-phase architecture: (1) free diff-based signals from existing data (headcount growth, tech stack changes, funding rounds), (2) Apify-powered signals (job postings, LinkedIn content analysis, profile changes), and (3) post-processing with dedup, scoring, and lead status updates. Writes signals to Supabase signals table for downstream activation.
npx gooseworks install --all # then, in Claude Code, Cursor, or Codex: /gooseworks use the signal-scanner skill
Scheduled scanner that detects buying signals on TAM companies and watchlist personas, writes them to the signals table, and sets up downstream activation.
SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY in .envAPIFY_TOKEN in .env (for Phase 2 signals)ANTHROPIC_API_KEY in .env (optional, for LLM content analysis)tam-builder| Priority | Signal | Level | Source | Cost |
|---|---|---|---|---|
| P0 | Headcount growth (>10% in 90d) | Company | Data diffs | Free |
| P0 | Tech stack changes | Company | Data diffs | Free |
| P0 | Funding round | Company | Data diffs | Free |
| P0 | Job posting for relevant roles | Company | Apify linkedin-job-search | ~$0.001/job |
| P1 | Leadership job change | Person | Apify linkedin-profile-scraper | ~$3/1k |
| P1 | LinkedIn content analysis | Person | Apify linkedin-profile-posts + LLM | ~$2/1k + LLM |
| P1 | LinkedIn profile updates | Person | Apify linkedin-profile-scraper | ~$3/1k |
| P2 | New C-suite hire | Company | Derived from person scans | Free |
See configs/example.json for full schema. Key sections:
client_name — which client's TAM to scansignals.* — enable/disable each signal type with thresholdsscan_scope — filter by tier, status, lead_statusCRITICAL: Never write signals or update lead statuses without explicit user approval.
The signal scanner writes to multiple tables: signals (insert), enrichment_log (insert), companies (patch snapshots), and people (patch lead_status). These writes affect downstream outreach decisions — bad signals lead to bad outreach timing.
Required flow:
--dry-run first to detect signals without writing to the database--dry-runWhy this matters:
lead_status changes from monitoring to signal_detected are hard to undo across many recordsThe agent must NEVER pass --yes on a first run. The --yes flag is only for pre-approved scheduled scans where the user has already validated the signal detection logic.
# Dry run first (ALWAYS DO THIS) — detect signals without writing to DB
python skills/capabilities/signal-scanner/scripts/signal_scanner.py \
--config skills/capabilities/signal-scanner/configs/my-client.json --dry-run
# Full scan (only after user reviews dry-run results and approves)
python skills/capabilities/signal-scanner/scripts/signal_scanner.py \
--config skills/capabilities/signal-scanner/configs/my-client.json
# Test mode (5 companies max)
python skills/capabilities/signal-scanner/scripts/signal_scanner.py \
--config configs/example.json --test --dry-run
# Free signals only (skip Apify)
# Set all Apify signals to enabled: false in config| Flag | Effect |
|---|---|
--config PATH | Path to config JSON (required) |
--test | Limit to 5 companies, 3 people |
--yes | Auto-confirm Apify cost prompts. Only use for pre-approved scheduled scans. |
--dry-run | Detect signals but don't write to DB. Always run this first. |
--max-runs N | Override Apify run limit (default 50) |
Each signal includes: client_name, company_id, person_id, signal_level (company or person), signal_type, signal_source, strength, signal_data (JSON), activation_score, detected_at, acted_on, run_id.
lead_status updated to signal_detected when activation_score >= thresholdmetadata._signal_snapshot updated for next diff cycleraw_data._signal_snapshot updated for next diff cycleenrichment_log entries with tool='apify', action='search' or 'enrich', plus credits_usedactivation_score = strength * recency_multiplier * account_fit
Recency: <24h = 1.5, 1-3d = 1.2, 3-7d = 1.0, 1-2w = 0.8, 2-4w = 0.5
Account: Tier 1 = 1.3, Tier 2 = 1.0, Tier 3 = 0.7tam-builder (provides companies + people)cold-email-outreach (acts on signals)signal-scanner/
├── SKILL.md
├── configs/
│ └── example.json
└── scripts/
└── signal_scanner.pySUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY in .envAPIFY_TOKEN in .env (for Phase 2 signals)Assemble an expert/educator motion-graphic LISTICLE video ad from a config — a spoken authoritative voiceover carries a numbered listicle while N web-animated hyperframe beats (HTML plus the Web Animations API, one branded design system of alternating tiles, big hero numerals, and glass-pill callouts) are rendered frame-by-frame via Playwright and anchored to the VO's word-level timestamps, periodic color-graded B-roll windows give visual breath, and captions burn ONLY inside those B-roll windows (2-word chunks, ASS Format header carrying a Name field so none drop) with the VO mixed under a low music bed. This is the FREE deterministic assembly stage (Playwright beat render plus ffmpeg concat plus window-masked caption burn plus VO-and-music mix plus final composite) — the VO, the music bed, and the stock B-roll come from create-vo-elevenlabs, create-music-elevenlabs, and media-proxy. Use for the vo-anchored-motion-listicle format.
Assemble a stop-motion hand-swatch-cycle product-demo ad from a config — a sequence of still PLATES (one hand swiping a single-barrel cosmetic across a cream skin-patch, the barrel + swatch changing per plate while the hand, background, crop, and lighting stay locked) is PNG→mp4 loop-encoded at each plate's own stop-motion hold (fast motion frames 150–250ms, per-shade ~380ms, hero beats 1100–1800ms), concat-demuxed with HARD cuts into a silent master, closed on a Playwright HTML-rendered branded end card (serif tagline + sans subtitle + real logo SVG over a hero BG, never AI-rendered text), and muxed with a pre-sourced music track playing under the end card with a fade tail (no VO). This is the FREE deterministic assembly stage (loop-encode + concat-demux + end-card render + music mux); the master-anchor plate, shade plates, and end-card BG come from create-image-gpt-image-fal and the track from create-music-elevenlabs. Use for the stopmotion-hand-swatch-cycle format.
Assemble a split-screen creator ad from a config — a two-zone vertical composite where a supplied AI-creator lip-sync take fills the BOTTOM ~48% while real 16:9 product/demo clips run uncropped in the TOP ~52%, each top clip contain-fit with a darkened blurred cover-scale fill of the same clip (never black bars), a 3px brand-color divider between the zones, the creator slice cover-fit per the per-scene VO timing, scenes hard-concatenated with the body audio being the concatenated creator VO slices, an end card held on the last sharp frame ~3s, then the ASSEMBLED cut transcribed with local Whisper (not the raw VO — concat drops inter-scene silence) and word-level captions burned in the chosen style. This is the FREE deterministic assembly + caption stage (two-zone composite + blurred fill + divider + hard-concat + end card + captions); the VO comes from create-vo-elevenlabs, the anchor from create-image-gpt-image-fal, and the whole-VO lip-sync from a paid VEED Fabric 1.0 take (a no-atom upstream input). Use for the split-screen-creator format.