The modern GTM stack is fragmented, expensive, and hostile to small teams: a dozen point tools, opaque pricing, data scattered across vendors. YALC is one open-source CLI plus IDE chat layer that owns the full outbound loop — prospecting, enrichment, qualification, campaigns, and email — under a single configuration. It's built for operators and founders who run their own outbound and want to keep their data on their own machine. Architecture splits cleanly: read context from your knowledge base, execute from this repo. Local-first by default, your data stays on your machine.
If the user pastes the repo URL (https://github.com/Othmane-Khadri/YALC-the-GTM-operating-system), drops a "what is this" / "let's start" / "let's go" / "how do I use this" / "i just cloned this" / "where do I begin" message, or otherwise signals first-touch intent, invoke the yalc-orientation skill (.claude/skills/yalc-orientation/SKILL.md).
If the user has a specific operational request — "import these leads", "launch a campaign for X", "scrape this LinkedIn post" — route directly to the matching capability or skill. Do NOT force them through orientation; they already know what they want.
Symptom: browser doesn't reach http://localhost:3847/setup/review.
Fix: confirm yalc-gtm start exited cleanly, copy the URL from the terminal banner, and check port 3847 isn't taken (lsof -i :3847).
Symptom: shell can't resolve the binary.
Fix: run npm i -g yalc-gtm-os (or pnpm link --global from the repo root if hacking on YALC itself).
Symptom: first-run paths absent or env file blank.
Fix: run yalc-gtm start to scaffold; the CLI writes the template and opens it for editing.
Open-source AI-native GTM operating system. Stack: Next.js 14, Tailwind, Drizzle + SQLite, Jotai, Anthropic SDK.
src/cli/index.ts— CLI entry point, all commands registered heresrc/lib/services/— external integrations (Crustdata, Unipile, FullEnrich, Instantly, Notion, Firecrawl, Slack)src/lib/qualification/— 7-gate lead qualification pipelinesrc/lib/campaign/— campaign creation, tracking, scheduling, intelligencesrc/lib/context/— context adapters (e.g., markdown-folder for reading external knowledge bases)src/lib/memory/— tenant memory store, embeddings, retrieval, dream cyclesrc/lib/framework/— GTM framework derivation from company contextsrc/lib/agents/— background agents, launchd integrationssrc/app/— Next.js web UI (chat, onboarding)~/.gtm-os/— per-tenant config, framework YAML, adaptersdocs/— architecture, commands, troubleshooting
npx tsx src/cli/index.ts <command> [options]
Env loaded from .env.local. Use --tenant <slug> or GTM_OS_TENANT env var for multi-tenant (default: default).
YALC ships with 24 curated lemlist Claude Code skills under .claude/skills/lemlist/, plus a flagship orchestration skill at .claude/skills/lemlist-campaign-from-icp/ and a lemlist MCP server pre-declared in .mcp.json. Together they cover the full outbound loop inside Claude Code: ICP → personas → sourcing → agentic enrichment → seniority-routed sequencing → quality gate → paused campaign in lemlist.
Flagship skill: lemlist-campaign-from-icp — turn one natural-language ICP prompt into a paused live lemlist campaign in ~5 minutes. Chains 24 atomic lemlist skills + the lemlist MCP. Hard approval gate before push. Landing page: https://yalc.ai/skills/lemlist-campaign-from-icp/.
| Layer | Lemlist atomic skills |
|---|---|
| Strategic foundation | icp-definer, persona-definer, pain-identifier, value-prop-lister, offer-definer, competitor-finder, trigger-finder |
| Sourcing | company-finder, list-builder, people-finder (+ MCP lemleads_search) |
| Per-lead angle | linkedin-outbound-angle |
| Campaign design | campaign-angle-finder, outbound-campaign-architect |
| Writing (seniority-routed) | copywriting-vp-sequence, copywriting-manager-sequence, copywriting-ic-sequence, copywriting-first-touch, copywriting-follow-up, cta-designer |
| Quality gate | copywriting-refiner, copywriting-analyzer, gtm-action-thinker |
| Loop (companion) | reply-handler, outbound-analyst |
MCP setup (one time, per machine):
Option A — API key (recommended for CI / scripted use):
export LEMLIST_API_KEY=... # generate in lemlist → Settings → Integrations
.mcp.json at the repo root reads LEMLIST_API_KEY from your environment. Restart Claude Code to pick it up.
Option B — OAuth (recommended for interactive use):
claude mcp add --transport http lemlist https://app.lemlist.com/mcp
Tokens refresh automatically for 30 days.
Verify with /mcp inside Claude Code — you should see the lemlist server connected and operations like create_campaign, lemleads_search, campaign stats, etc.
Affiliate signup if you don't have a lemlist account yet: https://get.lemlist.com/skrtwnkxw60i
- NEVER display API keys, tokens, or secrets from
.env.localin chat output. Mask all credentials.
- Public repo — use generic, descriptive commit messages. Never use prompt-like or task-specific messages.
Path-specific rules live in .claude/rules/. Claude Code auto-loads the relevant rule file when working in a matching directory.
| Rule File | Covers | Key Context |
|---|---|---|
.claude/rules/enrichment.md |
src/lib/enrichment/, src/lib/providers/ |
Provider registry, credit tracking, StepExecutor interface |
.claude/rules/qualification.md |
src/lib/qualification/ |
7-gate pipeline order, intelligence injection, gate configs |
.claude/rules/campaigns.md |
src/lib/campaign/ |
Message validation, rate limits, sequence timing, A/B testing |
.claude/rules/skills.md |
src/lib/skills/ |
Skill interface, RowBatch generator pattern, registry |
Adding new rules: Create a .md file in .claude/rules/ with an "Applies to" header listing the directories it covers, a "Context to Load" section with key files to read, and "Hard Rules" for non-negotiable constraints.
When the user expresses a durable rule, convention, or preference mid-session — phrases like "always X", "from now on", "we never Y", "remember that...", "for this project we..." — append it to the most specific matching file before continuing the task.
| What user said about | Where to save |
|---|---|
| Skill conventions, naming, validation, run patterns | .claude/rules/skills.md |
| Provider choices, MCP setup, credit policy, rate limits | .claude/rules/enrichment.md |
| Qualification gate behavior, ICP scoring, exclusion rules | .claude/rules/qualification.md |
| Campaign timing, message patterns, A/B testing rules | .claude/rules/campaigns.md |
| Repo-wide conventions, workflow, tooling | CLAUDE.md (this file), inserted under the most relevant section |
Write the rule in durable, generic wording — as a project rule, not a transcript. Bad: "User just said don't use HubSpot." Good: "Provider preference: do not configure HubSpot for this tenant."
If the user's preference doesn't match any of the above buckets, ask once: "Should I save this as a project rule in <file>?" Default to yes.
If the rule contradicts an existing line in the file, replace the old line and add a one-line (updated YYYY-MM-DD) annotation.
Project rules above describe how Claude Code should behave. Runtime context is different — it describes the user's company, ICP, voice, and outreach assets, and lives under ~/.gtm-os/. When the user shares context mid-session that should land in the GTM brain, route the change through the preview/commit flow, never write directly to the live file.
| When user says... | Save to (in _preview/) |
|---|---|
| "my voice is...", "tone should be..." | voice/tone-of-voice.md |
| "my ICP is...", "we sell to..." | company_context.yaml (icp.* fields) |
| "qualify a lead like X", "score this kind of lead..." | qualification_rules.md |
| "my outreach should...", "connect note should say..." | campaign_templates.yaml |
| "monitor this signal", "watch for X" | search_queries.txt |
| "we compete with X" | company_context.yaml (icp.competitors) |
| "our segment is...", "primary segment description..." | icp/segments.yaml |
Hard rule: runtime context modifications must go through _preview/ and a commit step. Never write directly to ~/.gtm-os/<live-file>. Use yalc-gtm start --regenerate <section> to refresh a section, then yalc-gtm start --commit-preview (optionally with --discard <section>) to promote it. If the user asks for an immediate edit, write the change into _preview/ and tell them to review + commit.
For per-tenant runs (--tenant acme), substitute ~/.gtm-os/tenants/acme/_preview/<file>.
For Earleads-specific client context (ICP, playbooks, battlecards), read from the Second Brain workspace configured as additionalDirectory. Client files: 01_Projects/Clients/Active/{ClientName}/.