AI Mindshare Map
Competitive intelligence tool: discover your audience, query AI systems, analyze responses, build competitive leaderboards, identify source gaps.
Command Syntax
/mindshare-map --entity <url> [--frame "..."] [--name <slug>] [--skip-validation] [--intents-per-track=N] [--intents=N]
Entity mode: tracks a specific company/brand.
/mindshare-map --market "<description>" [--frame "..."] [--name <slug>] [--skip-validation] [--intents-per-track=N] [--intents=N]
Market mode: landscape analysis without a focal entity.
Options:
--frame "..."Intent/persona dial — who is searching and why (default: buyers researching options)--name <slug>Work directory name (default: derived from URL domain or market description)--skip-validationSkip ICP confirmation (use inferred ICPs; buyer → default tracks, other → trackless)--intents-per-track=NIntents per track in tracked mode (default: 6)--intents=NIntents per ICP in trackless mode (default: 12)
Examples:
/mindshare-map --entity https://example.com→work/example-com-20260213//mindshare-map --market "creative agencies"→work/creative-agencies-20260213//mindshare-map --entity https://foo.com --name=foo-q1-2026→work/foo-q1-2026//mindshare-map --entity https://foo.com --frame "investors assessing category leaders"
Dependencies
- Playwright MCP: Audience discovery research
- WebFetch: Audience discovery research
- Bash: Running pipeline
- OpenAI API: GPT-5.2 for queries, GPT-5-mini for full-tier source assessment, GPT-5-nano for extraction/analysis/light-tier assessment
- tuff-lil-unit: Pipeline execution — crash recovery, step memoisation, concurrency
Work Directory
Each run gets a dated directory under work/:
work/creative-agencies-20260213/
audience-profile.json # ICPs + intents + target_focus (project identity)
subject-context.json # Research knowledge
tuff.db # SQLite: tuff steps + domain tables (queryable)
distillation.json # Full competitive landscape output
Outputs
- distillation.json — single file with full competitive landscape:
meta— schema version, run ID, mode, timestamp, query count, response rate, primary entity typeslandscape— query_answer entities grouped by type (company, product, …), ranked by reachother_entities— authority_source/platform entities grouped by typeby_icp— per-ICP breakdown:{ icp_001: { landscape: {…}, other_entities: {…} } }sources.market— by_publisher_type, per-domain assessments (each withsource_type,is_competitor,authority,citation_count,citation_count_by_icp)client— position, category_position, salience, visibility, reputation (entity mode only)
- tuff.db — queryable via
sqlite3: step status, token usage, domain tables
DB: detail behind distillation summaries
tuff.db retains full detail that distillation summarises. Query with sqlite3 tuff.db:
| Want | Table → column |
|---|---|
| Source authority reasoning + signals | assessments.result → authority |
| Source format + competitor relationship | assessments → source_type, is_competitor |
| All cited URLs per domain | assessments.result → urls |
| Per-response mention detail (salience, ICP, query) | analysed.mentions |
| Full GPT response text | queries.response |
| Fetched source page content | sources.content |
| Raw entity extractions per source | extractions.entities |
Setup (first run)
A SessionStart hook installs tuff-lil-unit into ${CLAUDE_PLUGIN_DATA} automatically and re-runs if package.json changes. Confirm OPENAI_API_KEY is set via shell (Keychain-backed recommended, exported from ~/.zshenv — see README). Playwright MCP is optional (entity-mode site research falls back to WebFetch without it).
If a pipeline phase fails with Could not locate the bindings file (a better-sqlite3 native-binding error), the install skipped its native build step. Fix: cd ${CLAUDE_PLUGIN_DATA} && npm rebuild better-sqlite3, then retry the phase.
Phase 0: Work Directory Setup
Before anything else, resolve the work directory:
- Derive slug from input:
--entity: extract domain, slugify →example-com--market: slugify description →creative-agencies--name=flag overrides (no date suffix added)
- Add date suffix:
${slug}-${YYYYMMDD}(e.g.,creative-agencies-20260213)- Skip if
--name=was provided (user controls full name)
- Skip if
- Set
WORK_DIR=work/${slug}-${YYYYMMDD}/ - Check if exists: if
WORK_DIRalready contains data, prompt user:work/creative-agencies-20260213/ already exists. Overwrite, or use a different name? - Create directory:
mkdir -p ${WORK_DIR}
All subsequent phases use --work-dir=${WORK_DIR}.
Phase 1: Audience Discovery
Subagent-driven research + user validation. Produces audience-profile.json.
Step 1.0: Frame
Capture --frame flag (default: buyer frame).
Step 1.1: Generate ICPs
Task({
subagent_type: 'general-purpose',
prompt: `Execute: references/${'entity' in subject
? 'entity-mode-instructions' : 'market-mode-instructions'}.md\n\n${
'entity' in subject ? `Entity URL: ${subject.entity}` : `Market: "${subject.market}"`}\n\nWORK_DIR: ${WORK_DIR} — write subject-context.json into this directory, not a hardcoded work/ path.`
})
Step 1.2: Validate ICPs
Present proposed ICPs to user. Collect feedback, refine if needed. Skip if --skip-validation.
If discovery returned research_limited (entity mode, a JS-rendered site WebFetch couldn't read), tell the user — even under --skip-validation:
{entity}looks JS-rendered, so these ICPs lean more on general web research than on its site. Installing Playwright MCP would let me read the full site — add it and re-run discovery, or proceed with these?
Step 1.2a: Track Selection
After ICPs are confirmed, decide market-mapping tracks:
| Frame | With --skip-validation | Interactive |
|---|---|---|
| Buyer | Default tracks (4) | Offer defaults or trackless |
| Custom | Trackless | Propose custom tracks or trackless |
Default tracks: landscape, recommendation, specialization, scenario. All tracks must be entity-surfacing. See references/intent-query-generation-instructions.md for phrasing guidance.
Step 1.3: Generate Intents + Queries
One subagent per ICP, in parallel. Build a roleplay brief for each ICP using three neutral slots interpreted through the frame:
| Slot | Buyer | Investor | Journalist |
|---|---|---|---|
motivations | goals | investment thesis | beat and angle |
constraints | pain points | due diligence concerns | editorial constraints |
context | current situation | portfolio context | current assignments |
Record the constructed brief in the ICP's brief block in audience-profile.json before sending to the subagent.
Use tracked or trackless template from references/intent-query-generation-instructions.md. Tracked: --intents-per-track=N (default 6), intent JSON includes track. Trackless: --intents=N (default 12), track omitted.
const icpResults = await Promise.all(confirmedICPs.map(icp =>
Task({
subagent_type: 'general-purpose',
model: 'sonnet',
prompt: buildRoleplayBrief(icp, confirmedTracks, trackless)
})
))
Step 1.3b: Query Review
Single review subagent (Sonnet) scans all generated queries for methodology problems. Returns flagged queries with reasons; orchestrator fixes or drops before presenting to user.
Task({
subagent_type: 'general-purpose',
model: 'sonnet',
prompt: `Review these generated queries for research methodology problems...\n${allQueries}`
// See references/query-review-instructions.md for criteria and output format
})
Step 1.4: Validate Intents + Write Profile
Present intents to user, collect feedback. Write ${WORK_DIR}/audience-profile.json. Skip validation if --skip-validation.
Phase 2: Pipeline (task-based, background jobs)
Step 2.1: Create Tasks + Set Dependencies
const query = TaskCreate({
subject: "Query GPT for all ICPs",
description: "Bash({ run_in_background: true, command: 'NODE_PATH=${CLAUDE_PLUGIN_DATA}/node_modules npm run --prefix ${CLAUDE_PLUGIN_ROOT} pipeline:query -- --work-dir=${WORK_DIR}' })",
activeForm: "Querying GPT"
})
const fetch = TaskCreate({
subject: "Fetch cited URLs",
description: "Bash({ run_in_background: true, command: 'NODE_PATH=${CLAUDE_PLUGIN_DATA}/node_modules npm run --prefix ${CLAUDE_PLUGIN_ROOT} pipeline:fetch -- --work-dir=${WORK_DIR}' })",
activeForm: "Fetching URLs"
})
const extract = TaskCreate({
subject: "Extract entities from sources",
description: "Bash({ run_in_background: true, command: 'NODE_PATH=${CLAUDE_PLUGIN_DATA}/node_modules npm run --prefix ${CLAUDE_PLUGIN_ROOT} pipeline:extract -- --work-dir=${WORK_DIR}' })",
activeForm: "Extracting entities"
})
const assess = TaskCreate({
subject: "Assess source authority",
description: "Bash({ run_in_background: true, command: 'NODE_PATH=${CLAUDE_PLUGIN_DATA}/node_modules npm run --prefix ${CLAUDE_PLUGIN_ROOT} pipeline:assess -- --work-dir=${WORK_DIR}' })",
activeForm: "Assessing sources"
})
const analyse = TaskCreate({
subject: "Analyse GPT responses",
description: "Bash({ run_in_background: true, command: 'NODE_PATH=${CLAUDE_PLUGIN_DATA}/node_modules npm run --prefix ${CLAUDE_PLUGIN_ROOT} pipeline:analyse -- --work-dir=${WORK_DIR}' })",
activeForm: "Analysing responses"
})
const distill = TaskCreate({
subject: "Distill competitive landscape",
description: "Bash({ run_in_background: true, command: 'NODE_PATH=${CLAUDE_PLUGIN_DATA}/node_modules npm run --prefix ${CLAUDE_PLUGIN_ROOT} pipeline:distill -- --work-dir=${WORK_DIR}' }). Outputs: distillation.json",
activeForm: "Distilling landscape"
})
// Dependency graph:
// Query → Fetch → Extract → Assess ─┐
// └──→ Analyse ────────────────────┴→ Distill
TaskUpdate({ taskId: fetch.id, addBlockedBy: [query.id] })
TaskUpdate({ taskId: extract.id, addBlockedBy: [fetch.id] })
TaskUpdate({ taskId: assess.id, addBlockedBy: [extract.id] })
TaskUpdate({ taskId: analyse.id, addBlockedBy: [query.id] })
TaskUpdate({ taskId: distill.id, addBlockedBy: [assess.id, analyse.id] })
Step 2.2: Execute Task Graph
Begin with query. On each completion: mark task done, start unblocked dependents.
Monitoring progress — each phase writes to DB per-item. Check progress any time:
NODE_PATH=${CLAUDE_PLUGIN_DATA}/node_modules npm run --prefix ${CLAUDE_PLUGIN_ROOT} pipeline:progress -- --work-dir=${WORK_DIR} query # single phase: "query: 24/24"
NODE_PATH=${CLAUDE_PLUGIN_DATA}/node_modules npm run --prefix ${CLAUDE_PLUGIN_ROOT} pipeline:progress -- --work-dir=${WORK_DIR} # all phases
On crash or interruption: re-run the failed phase — tuff step memoisation skips completed items.
Full pipeline (non-interactive): NODE_PATH=${CLAUDE_PLUGIN_DATA}/node_modules npm run --prefix ${CLAUDE_PLUGIN_ROOT} pipeline -- --work-dir=${WORK_DIR} runs all 6 phases sequentially.
Phase 3: Delivery
Once pipeline completes, present outputs to user:
**AI Mindshare Map complete** → `${WORK_DIR}/`
**Outputs:**
- `${WORK_DIR}/distillation.json` — competitive landscape data
- `${WORK_DIR}/audience-profile.json` — ICPs + intents
**Key Findings:**
- Entities discovered: {{entities_discovered}}
- Source-backed: {{source_backed_count}}
- Target rank: #{{rank}} of {{rank_of}} *(entity mode only)*