Skip to content

feat(gtm): add /gtm command for cross-channel go-to-market planning#141

Merged
auroracapital merged 1 commit intomainfrom
claude/add-gtm-command-WpWDq
Apr 18, 2026
Merged

feat(gtm): add /gtm command for cross-channel go-to-market planning#141
auroracapital merged 1 commit intomainfrom
claude/add-gtm-command-WpWDq

Conversation

@auroracapital
Copy link
Copy Markdown
Collaborator

@auroracapital auroracapital commented Apr 18, 2026

Introduces the ops-gtm skill as a strategy layer on top of /marketing.
Generates a GTM plan across paid, unpaid, sales, and AI-automation
avenues, then hands executable items off to /marketing via the Skill
tool so credential resolution and API calls stay single-sourced.

https://claude.ai/code/session_01RNfoAVC7WcLQwuhu24Q1EE


Open in Devin Review

Note

Medium Risk
Introduces a new high-level orchestration skill that writes plan artifacts and can trigger downstream /marketing executions, so incorrect routing/prompting could lead to unintended campaign actions despite per-item confirmation gates.

Overview
Adds a new ops-gtm skill (skills/ops-gtm/SKILL.md) that guides users through GTM intake, generates full or channel-specific plans (paid/unpaid/sales/automation/launch/brief), and persists dated plan/brief files under ${CLAUDE_PLUGIN_DATA_DIR}/gtm/ without overwriting.

Defines explicit execution handoff paths that map plan items to /marketing sub-commands (or manual steps), including an approval-driven flow that delegates launchable items to Skill("ops-marketing", ...) while enforcing plugin rules around ≤4-option prompts and explicit confirmation for paid/outbound actions.

Reviewed by Cursor Bugbot for commit 8726ac4. Bugbot is set up for automated code reviews on this repo. Configure here.

Introduces the ops-gtm skill as a strategy layer on top of /marketing.
Generates a GTM plan across paid, unpaid, sales, and AI-automation
avenues, then hands executable items off to /marketing via the Skill
tool so credential resolution and API calls stay single-sourced.

https://claude.ai/code/session_01RNfoAVC7WcLQwuhu24Q1EE
@auroracapital auroracapital requested a review from Copilot April 18, 2026 14:44
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 18, 2026

Warning

Rate limit exceeded

@auroracapital has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 53 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 6 minutes and 53 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9f292583-c23c-4c5f-886f-669005c08962

📥 Commits

Reviewing files that changed from the base of the PR and between 3b1b4ef and 8726ac4.

📒 Files selected for processing (1)
  • claude-ops/skills/ops-gtm/SKILL.md
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/add-gtm-command-WpWDq

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@auroracapital auroracapital merged commit 28c991a into main Apr 18, 2026
11 of 12 checks passed
@auroracapital auroracapital deleted the claude/add-gtm-command-WpWDq branch April 18, 2026 14:45

```bash
# Existing prefs
jq -r '{project: .gtm_default_project, audience: .gtm_default_audience, voice: .gtm_brand_voice, budget: .gtm_monthly_budget, stage: .gtm_stage}' "$PREFS_PATH" 2>/dev/null
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The shell variable $PREFS_PATH is used in bash snippets without being defined, causing preference read and write operations to silently fail.
Severity: HIGH

Suggested Fix

Define the PREFS_PATH variable at the beginning of the skill file, similar to other skills. Add the line: PREFS_PATH="${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}/preferences.json" to ensure the correct path is used for preference operations.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: claude-ops/skills/ops-gtm/SKILL.md#L313

Potential issue: The `ops-gtm/SKILL.md` file uses the shell variable `$PREFS_PATH` in
multiple bash snippets without defining it. When these snippets are executed,
`$PREFS_PATH` expands to an empty string. Consequently, commands like `jq` that attempt
to read from or write to the preferences file will target an empty file path and
silently fail. This prevents the `/gtm setup` command from reading existing preferences
or persisting new user configurations, effectively breaking the skill's ability to
remember settings across sessions.

Did we get this right? 👍 / 👎 to inform future reviews.

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 4 additional findings in Devin Review.

Open in Devin Review

4. **Monthly budget tier** — `[<$1k, $1k–5k, $5k–25k, $25k+]`
5. **Brand voice** — `[Playful, Professional, Technical, Bold]`

Save to `$PREFS_PATH` as `gtm_default_project`, `gtm_project_type`, `gtm_stage`, `gtm_monthly_budget`, `gtm_brand_voice`. Never write API keys here — GTM does not own any credentials (`/marketing` does).
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Preference key mismatch: setup saves gtm_project_type but Runtime Context reads gtm_default_audience

The setup section (line 329) saves the Project Type answer under the key gtm_project_type, but this key is never read anywhere. Meanwhile, the Runtime Context (line 32) and the setup auto-scan jq command (line 313) both read gtm_default_audience, which is never saved by any flow. This means:

  1. Running /gtm setup and answering "Project type" writes to a dead key (gtm_project_type) — subsequent runs never pick it up.
  2. The gtm_default_audience preference is always null/missing, so the audience default never works despite the sub-command routing table (line 77) claiming setup configures "audience".
Key mismatch detail
  • Read sites: claude-ops/skills/ops-gtm/SKILL.md:32 lists gtm_default_audience as a readable pref; claude-ops/skills/ops-gtm/SKILL.md:313 jq command reads .gtm_default_audience.
  • Write site: claude-ops/skills/ops-gtm/SKILL.md:329 saves gtm_project_type.
  • Neither key cross-references the other.
Prompt for agents
The preference keys are inconsistent between the setup save operation and the Runtime Context / auto-scan read operations. The setup section at line 329 saves gtm_project_type, but the Runtime Context at line 32 and the jq auto-scan at line 313 read gtm_default_audience. One of these must be corrected to match the other. Additionally, there is no actual audience question in the setup flow (lines 322-327), yet the routing table at line 77 claims setup configures audience. The fix likely involves either: (a) renaming gtm_project_type to gtm_default_audience everywhere and reframing the project type question as an audience question, or (b) adding a separate audience question to setup and updating the Runtime Context and jq to also read gtm_project_type, or (c) deciding which concept (project type vs audience) is the correct one and aligning all references.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


Save to `$PREFS_PATH` as `gtm_default_project`, `gtm_project_type`, `gtm_stage`, `gtm_monthly_budget`, `gtm_brand_voice`. Never write API keys here — GTM does not own any credentials (`/marketing` does).

Finish with a smoke test: run `/gtm brief` in background and report `✓ setup complete — try /gtm plan` or `✗ [error]`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Severity 7/10 — Smoke test is self-referential and background-incompatible

/gtm brief requires intake questions (AskUserQuestion calls for project name, ICP, etc.). Running it with run_in_background: true means:

  1. The sub-command will immediately fail or hang — no user can respond to interactive prompts in a background task.
  2. Even if it were fire-and-forget, the result (✓/✗) is reported before the background task completes, making the status always stale/incorrect.

Suggested fix — replace the self-invocation with a lightweight prefs-validation check:

jq -e '.gtm_default_project and .gtm_stage' "$PREFS_PATH" > /dev/null 2>&1 \
  && echo "✓ setup complete — try /gtm plan" \
  || echo "✗ preferences write failed — check $PREFS_PATH"

This verifies the actual output of the setup flow without recursively invoking another sub-command.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is ON, but it could not run because the branch was deleted or merged before autofix could start.

Reviewed by Cursor Bugbot for commit 8726ac4. Configure here.

4. **Monthly budget tier** — `[<$1k, $1k–5k, $5k–25k, $25k+]`
5. **Brand voice** — `[Playful, Professional, Technical, Bold]`

Save to `$PREFS_PATH` as `gtm_default_project`, `gtm_project_type`, `gtm_stage`, `gtm_monthly_budget`, `gtm_brand_voice`. Never write API keys here — GTM does not own any credentials (`/marketing` does).
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preference key mismatch between setup save and read

Medium Severity

The setup sub-command saves gtm_project_type to preferences, but the Runtime Context section and the setup auto-scan jq command both read gtm_default_audience instead — a key that is never written anywhere. This means "Project type" chosen during setup is silently discarded on subsequent runs, and the gtm_default_audience preference will always be empty, causing the intake flow to re-ask for information the user already provided.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8726ac4. Configure here.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new ops-gtm skill that acts as a go-to-market strategy layer over ops-marketing, generating cross-channel GTM plans and delegating executable work to /marketing via the Skill tool.

Changes:

  • Introduces ops-gtm skill definition and operating instructions (intake, planning flow, channel catalog, handoff flow).
  • Adds sub-command structure for targeted GTM outputs (plan, paid, unpaid, sales, automation, launch, brief, setup).
  • Defines integration points that hand execution off to ops-marketing rather than duplicating credential/API logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +328 to +329

Save to `$PREFS_PATH` as `gtm_default_project`, `gtm_project_type`, `gtm_stage`, `gtm_monthly_budget`, `gtm_brand_voice`. Never write API keys here — GTM does not own any credentials (`/marketing` does).
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setup says it saves gtm_project_type, but earlier sections reference gtm_default_audience and instruct intake to read gtm_default_* keys. This looks inconsistent (audience is never collected/saved; project type key name doesn’t match the gtm_default_* pattern). Either add an audience question and save it, or remove the gtm_default_audience references; and standardize the preference key names so intake/setup agree.

Suggested change
Save to `$PREFS_PATH` as `gtm_default_project`, `gtm_project_type`, `gtm_stage`, `gtm_monthly_budget`, `gtm_brand_voice`. Never write API keys here — GTM does not own any credentials (`/marketing` does).
6. **Default audience** — free-text, e.g. `SMB founders`, `Developers`, or `Consumers`
Save to `$PREFS_PATH` as `gtm_default_project`, `gtm_default_project_type`, `gtm_default_stage`, `gtm_default_monthly_budget`, `gtm_default_brand_voice`, `gtm_default_audience`. Never write API keys here — GTM does not own any credentials (`/marketing` does).

Copilot uses AI. Check for mistakes.
|---|---|---|---|
| Programmatic SEO | Dev tools, marketplaces, comparison queries | High upfront, compounding | `/marketing seo` (tracking) + manual content ops |
| Topic-cluster SEO | Content-led SaaS, info-intent | Medium, 3–6mo to signal | `/marketing seo` |
| Lifecycle email (welcome, nurture, winback) | Any with email capture | Medium, high leverage | `/marketing email` (Klaviyo flows) |
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Unpaid catalog and later sections imply /marketing email can scaffold lifecycle flows (e.g., "Klaviyo flows"), but claude-ops/skills/ops-marketing/SKILL.md only documents email metrics/inspection (no flow creation/scaffolding endpoints or commands). As-is, the handoff to /marketing email won’t produce the promised artifacts. Either adjust the wording to match what /marketing email actually does today, or add a corresponding flow-scaffolding capability to /marketing and reference the correct sub-command here.

Suggested change
| Lifecycle email (welcome, nurture, winback) | Any with email capture | Medium, high leverage | `/marketing email` (Klaviyo flows) |
| Lifecycle email (welcome, nurture, winback) | Any with email capture | Medium, high leverage | `/marketing email` (audit/metrics) + manual Klaviyo flow build |

Copilot uses AI. Check for mistakes.
Comment on lines +54 to +57
Agent(team_name="gtm-research-team", name="paid-research", prompt="Research paid acquisition channels that fit ${PROJECT_TYPE} at ${STAGE} with $${MONTHLY_BUDGET}/mo. Return a ranked list with fit signals, expected CAC, and which /marketing sub-command (if any) executes each channel.")
Agent(team_name="gtm-research-team", name="unpaid-research", prompt="Research organic channels (SEO, content, community, PR, partnerships, referrals, lifecycle email) for ${PROJECT_TYPE} at ${STAGE}. Return ranked list with effort estimate, time-to-signal, and /marketing sub-command mapping.")
Agent(team_name="gtm-research-team", name="sales-research", prompt="Design a sales motion for ${PROJECT_TYPE}: outbound vs inbound vs PLG vs channel. Propose a 30/60/90 plan with concrete activities and target metrics.")
Agent(team_name="gtm-research-team", name="automation-research", prompt="Propose AI-automation recipes: lead enrichment, personalized outreach, content ops, lifecycle copy, support deflection, lead scoring. For each, list trigger, model/tool stack, and where it plugs into /marketing.")
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Agent Team prompt examples include shell-style placeholders like ${PROJECT_TYPE}, ${STAGE}, and ${MONTHLY_BUDGET}, but these variables are never defined elsewhere in the skill. If passed through literally, the subagents will see the raw placeholders instead of the actual intake values. Consider either interpolating the real intake values before calling Agent(...), or switch the examples to neutral placeholders like [project_type], [stage], [monthly_budget] and explicitly instruct to substitute them.

Copilot uses AI. Check for mistakes.
Comment on lines +68 to +72
| Input | Action |
|---|---|
| (empty), plan | Full GTM plan across all four avenues |
| paid | Paid acquisition deep-dive (Meta, Google, LinkedIn, TikTok, affiliates, sponsorships) |
| unpaid | Organic deep-dive (SEO, content, community, PR, partnerships, referrals, lifecycle email) |
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Markdown tables here start each row with || (double pipe), which renders an extra empty column in most Markdown renderers. Update the table syntax to use a single leading | (and make the same adjustment to the other tables in this file) so the routing table renders correctly.

Copilot uses AI. Check for mistakes.
Comment on lines +111 to +117
| Channel | Fits | Cost profile | Execution |
|---|---|---|---|
| Meta Ads (Facebook + Instagram) | B2C, marketplace, broad consumer | $5–50 CPA typical | `/marketing ads` · `/marketing meta create-campaign` |
| Google Ads — Search | High-intent buyers, existing demand | $1–30 CPC | `/marketing google-ads` |
| Google Ads — Performance Max | E-comm with catalog | Blended CPA | `/marketing google-ads` |
| YouTube Ads | Awareness at scale | $0.01–0.30 CPV | `/marketing google-ads` (video campaigns) |
| LinkedIn Ads | B2B, ACV > $10k | $8–15 CPC, $50+ CPL | manual — LinkedIn Campaign Manager |
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The channel catalog tables also use || at the start of rows, which will create an unintended blank first column when rendered. Standardize these tables to normal Markdown | ... | formatting for readability (this applies to Paid/Unpaid/Sales/AI Automation tables).

Copilot uses AI. Check for mistakes.
Before producing any plan section, make sure the following are known. Source them in this order — only ask the user for what's still missing.

1. **Auto-scan the repo** (step 4 of Runtime Context).
2. **Read prefs** for `gtm_default_*` keys.
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Project intake says to read prefs for gtm_default_* keys, but the rest of the document uses non-default keys like gtm_stage / gtm_monthly_budget (and setup later saves gtm_project_type). As written, setup values may not be picked up during intake. Align the preference key naming (either read the specific keys you write in setup, or rename what setup writes to match the gtm_default_* convention).

Suggested change
2. **Read prefs** for `gtm_default_*` keys.
2. **Read prefs** for `gtm_project_type`, `gtm_stage`, `gtm_primary_goal`, and `gtm_monthly_budget` (fall back to legacy `gtm_default_*` keys if present).

Copilot uses AI. Check for mistakes.
- `[Launch via /marketing]` — hand top executable items to `/marketing`
- `[Save only]` — keep the plan file, take no action
- `[Refine plan]` — re-run with adjusted intake
- `[Schedule follow-up]` — revisit in 7/30 days (delegate to `/ops cron` if available)
Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The handoff option suggests delegating to /ops cron, but there does not appear to be a corresponding skill/command in this repo (no /ops:cron or ops-cron skill). This would fail at runtime when selected. Consider delegating to an existing mechanism (e.g., ops-daemon / daemon-services scheduling) or remove/replace this option with a supported command.

Suggested change
- `[Schedule follow-up]` — revisit in 7/30 days (delegate to `/ops cron` if available)
- `[Schedule follow-up]` — revisit in 7/30 days; if a supported scheduling mechanism is available, use it, otherwise save the reminder details in the plan and tell the user to schedule it with their preferred tool

Copilot uses AI. Check for mistakes.
Comment on lines +311 to +314
```bash
# Existing prefs
jq -r '{project: .gtm_default_project, audience: .gtm_default_audience, voice: .gtm_brand_voice, budget: .gtm_monthly_budget, stage: .gtm_stage}' "$PREFS_PATH" 2>/dev/null

Copy link

Copilot AI Apr 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setup section’s shell snippet references $PREFS_PATH, but this variable isn’t defined anywhere in the document. Other skills typically define it as ${CLAUDE_PLUGIN_DATA_DIR:-$HOME/.claude/plugins/data/ops-ops-marketplace}/preferences.json before use. Define PREFS_PATH (or inline the full path consistently) to avoid ambiguous instructions and potential mis-writes.

Copilot uses AI. Check for mistakes.
auroracapital added a commit that referenced this pull request Apr 18, 2026
Minor release bundling three feature PRs and multiple security/stability fixes:

- PR #141: /gtm — cross-channel go-to-market planning skill
- PR #139: /ops:projects portfolio dashboard + GSD registry sync
- PR #140: ops-speedup v2 parity (GPU/ANE monitoring, power hogs, OS actions)
- PR #138: ops-memory-extractor Claude Code OAuth support + wacli persistent --follow fix

Bug fixes (see CHANGELOG.md [1.7.0] for full detail):
- SEV-9: ops-speedup eval shell-injection (Seer)
- SEV-9: ops-projects hardcoded /Users/ path breaking for all other users (Seer + blocksorg + cursor + devin + codex)
- SEV-8: ops-speedup RETURN trap race + systemd mask allowlist
- SEV-7: ops-speedup lsof +D probe wedge, daemon-services backing-script gap, ops-projects AskUserQuestion allowed-tools mismatch
- wacli --follow torn down by immediate backfill (now INITIAL_BACKFILL_DELAY=30 + reentrant guard)

Bumps claude-ops/package.json, claude-ops/.claude-plugin/plugin.json, and
.claude-plugin/marketplace.json plugins[0].version 1.6.2 → 1.7.0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants