[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M751ADWH2GMZXNWYQKGSMB27":3,"skill-same:01M751ADWH2GMZXNWYQKGSMB27":49},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":26,"commitSha":27,"skillDirectory":28,"forksCount":29,"repositoryHtmlUrl":30,"repositoryHomepageUrl":31,"ownerHtmlUrl":32,"ownerBlogUrl":31,"ownerDescription":33,"updatedAt":34,"install":35,"resources":48},"01M751ADWH2GMZXNWYQKGSMB27","pandas-expert",null,"Writes correct, vectorized pandas for cleaning, joining, reshaping, and aggregating tabular data, with validated joins and deliberate dtype and missing-data handling. Use when someone asks \"why did my merge duplicate rows\", \"how do I clean this CSV in pandas\", \"my groupby numbers look wrong\", \"this apply is too slow\", or is transforming a DataFrame for analysis or a pipeline. Do NOT use for first-pass profiling of an unfamiliar dataset - use eda-playbook instead; for data too large for one machine use spark-jobs; for answering business questions directly in SQL use sql-to-insights.","SkillMedev\u002Fskills","SkillMedev","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F298078254?v=4","01M74ZSGTQ918CB0VXW4EG3RMX","SkillMe","01M74ZSGVBT497NA95CBT8JTY4",25,"MIT","https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fpandas-expert\u002FSKILL.md",[],[],"2026-11-11 18:11:01",false,"---\nname: pandas-expert\ndescription: Writes correct, vectorized pandas for cleaning, joining, reshaping, and aggregating tabular data, with validated joins and deliberate dtype and missing-data handling. Use when someone asks \"why did my merge duplicate rows\", \"how do I clean this CSV in pandas\", \"my groupby numbers look wrong\", \"this apply is too slow\", or is transforming a DataFrame for analysis or a pipeline. Do NOT use for first-pass profiling of an unfamiliar dataset - use eda-playbook instead; for data too large for one machine use spark-jobs; for answering business questions directly in SQL use sql-to-insights.\nmetadata:\n  title: \"Pandas Expert\"\n---\n\n# Pandas Expert\n\nMost pandas bugs are silent: a join that fans out rows, a dtype that upcast to object, a NaN that vanished from a sum. The output looks plausible and is wrong. This skill produces transformations that validate themselves - every join asserted, every coercion counted, every aggregate reconciled - so errors surface at the line that caused them instead of in a dashboard three weeks later.\n\n## Operating procedure\n\nFollow the steps in order. Types must be fixed before cleaning, cleaning before joining, joining before aggregating - each later step silently produces wrong numbers if an earlier one was skipped.\n\n### Step 1: gather inputs\n\nBefore writing any transformation, establish:\n\n- The grain of each table: what one row represents. If the user cannot state it, derive it with `df.duplicated(subset=candidate_keys).sum()` and label the result a guess until confirmed.\n- The expected output: shape, grain, and one or two spot-check numbers the user already trusts (last month's total, a known customer's count).\n- Whether the source can be mutated. Default to no: work on a copy, never mutate the caller's DataFrame.\n- Approximate size. Pandas wants roughly 3-5x the on-disk dataset size in free RAM for comfortable transformation work. Above a few GB in memory, reach for chunked reads (`pd.read_csv(..., chunksize=...)`), pyarrow-backed dtypes, or hand off to spark-jobs.\n\n### Step 2: inspect before touching\n\nRun `df.info()`, `df.describe()`, `df.isna().sum()`, and `df.nunique()` first. You are looking for: object columns that should be numeric or datetime, null counts, and cardinality. This is a two-minute sanity pass, not a full audit - for a genuinely unfamiliar dataset, run eda-playbook first.\n\n### Step 3: fix dtypes early\n\n- Parse dates with `pd.to_datetime`; pass `format=` when known (it is faster and catches malformed rows), `format=\"mixed\"` only when formats genuinely vary.\n- Coerce numerics with `pd.to_numeric(col, errors=\"coerce\")`, then **count and report** what failed: `col.isna().sum()`. Silent coercion is how \"N\u002FA\" strings become invisible revenue holes.\n- Cast low-cardinality strings to `category` when distinct values are under roughly 50% of row count - it cuts memory and speeds groupbys.\n- Downcast large numeric columns (`pd.to_numeric(col, downcast=\"integer\")`) when memory is tight.\n\n### Step 4: handle missing data deliberately\n\nChoose drop, fill, or flag - and state which and why in a comment. If more than ~5% of a key column is null, stop and ask whether the nulls are structural (a pipeline branch) before imputing; that determination changes the right treatment entirely.\n\n### Step 5: join with validation, never on faith\n\n- Always state `how=` and `on=` explicitly.\n- Always pass `validate=` (\"one_to_one\", \"many_to_one\", \"many_to_many\") so pandas raises on unexpected key duplication instead of silently fanning out.\n- After the join, assert the row count: a left join must preserve `len(left)`.\n- Check match rate with `indicator=True` when a low match rate would be a data problem.\n\n### Step 6: reshape and aggregate\n\n- `pivot_table` for long→wide, `melt` for wide→long.\n- `groupby().agg()` with **named aggregations** - `agg(revenue=(\"amount\", \"sum\"))` - so output columns are self-describing.\n- Remember `sum()` skips NaN silently; if a NaN-contaminated group should show as missing, use `min_count=1`.\n\n### Step 7: verify the output\n\nReconcile at least one aggregate against the pre-transformation source (total rows, total revenue). Show the transformation step by step with a comment on each step explaining intent, not mechanics.\n\n## Worked example\n\nMessy orders joined to a customer table with a duplicate key - the validation catches it:\n\n```python\nimport pandas as pd\n\norders = pd.DataFrame({\n    \"order_id\": [1001, 1002, 1003, 1004, 1005],\n    \"customer_id\": [\"C01\", \"C02\", \"C01\", \"C03\", \"C02\"],\n    \"amount\": [\"120.50\", \"80.00\", \"N\u002FA\", \"45.25\", \"200.00\"],\n})\ncustomers = pd.DataFrame({\n    \"customer_id\": [\"C01\", \"C02\", \"C03\", \"C03\"],   # C03 duplicated upstream\n    \"region\": [\"West\", \"East\", \"South\", \"South-dup\"],\n})\n\n# Coerce and count failures instead of letting them vanish\norders[\"amount\"] = pd.to_numeric(orders[\"amount\"], errors=\"coerce\")\nprint(orders[\"amount\"].isna().sum(), \"amounts failed to parse\")\n\n# validate= raises instead of silently duplicating order 1004\ntry:\n    orders.merge(customers, on=\"customer_id\", how=\"left\", validate=\"many_to_one\")\nexcept pd.errors.MergeError as e:\n    print(\"MergeError:\", str(e).splitlines()[0])\n\ncustomers = customers.drop_duplicates(\"customer_id\", keep=\"first\")\nmerged = orders.merge(customers, on=\"customer_id\", how=\"left\", validate=\"many_to_one\")\nassert len(merged) == len(orders)\n\nsummary = (\n    merged.groupby(\"region\", as_index=False)\n    .agg(orders=(\"order_id\", \"count\"), revenue=(\"amount\", \"sum\"))\n    .sort_values(\"revenue\", ascending=False)\n)\nprint(summary.to_string(index=False))\n```\n\nOutput:\n\n```\n1 amounts failed to parse\nMergeError: Merge keys are not unique in right dataset; not a many-to-one merge\nregion  orders  revenue\n  East       2   280.00\n  West       2   120.50\n South       1    45.25\n```\n\nRead it: the \"N\u002FA\" string was counted, not swallowed; the duplicate C03 row raised at the merge line instead of inflating South's revenue; West shows 2 orders but only 120.50 revenue because the failed parse became NaN and `sum` skipped it - a fact the failure count already disclosed.\n\n## Performance rules\n\n- Avoid `apply` over rows when a vectorized op, `np.where`, or `np.select` exists - vectorized versions are commonly 10-100x faster.\n- Avoid chained indexing (`df[a][b] = ...`); use `.loc[rows, cols]` for all assignment.\n- Watch for silent upcasts to `object` dtype after concatenation or fillna - they destroy both speed and memory.\n- Prefer `merge` over row-wise lookups; prefer `groupby().transform` over apply-per-group when the output is row-aligned.\n\n## Deliverable\n\nProduce a runnable, commented transformation script (or notebook cells) containing: the dtype-fixing block with coercion-failure counts, every join carrying `how=`, `on=`, `validate=`, and a row-count assertion, named aggregations, and a final reconciliation check against a number the user already trusts.\n\n## Do NOT\n\n- Do not merge without `validate=` - fan-out from duplicated keys is the single most common source of inflated metrics.\n- Do not use `errors=\"coerce\"` without counting what was coerced; a clean-looking column can hide hundreds of destroyed values.\n- Do not use `apply` with a lambda for arithmetic a vectorized expression can do.\n- Do not mutate the input DataFrame in place inside a function; copy when in doubt.\n- Do not trust `sum()` on a column with NaN to mean \"complete total\" - it means \"total of what parsed\".\n- Do not profile an unknown dataset with ad-hoc snippets; run eda-playbook and come back with its decision log.\n\n## Quality bar\n\nA finished transformation ships only when: every join is validated and row-count-asserted; every type coercion reports its failure count; at least one output aggregate reconciles to a trusted source number; no chained indexing or row-wise `apply` remains where a vectorized form exists; and a reader can follow the intent from comments without running the code.\n","# Pandas Expert\n\nMost pandas bugs are silent: a join that fans out rows, a dtype that upcast to object, a NaN that vanished from a sum. The output looks plausible and is wrong. This skill produces transformations that validate themselves - every join asserted, every coercion counted, every aggregate reconciled - so errors surface at the line that caused them instead of in a dashboard three weeks later.\n\n## Operating procedure\n\nFollow the steps in order. Types must be fixed before cleaning, cleaning before joining, joining before aggregating - each later step silently produces wrong numbers if an earlier one was skipped.\n\n### Step 1: gather inputs\n\nBefore writing any transformation, establish:\n\n- The grain of each table: what one row represents. If the user cannot state it, derive it with `df.duplicated(subset=candidate_keys).sum()` and label the result a guess until confirmed.\n- The expected output: shape, grain, and one or two spot-check numbers the user already trusts (last month's total, a known customer's count).\n- Whether the source can be mutated. Default to no: work on a copy, never mutate the caller's DataFrame.\n- Approximate size. Pandas wants roughly 3-5x the on-disk dataset size in free RAM for comfortable transformation work. Above a few GB in memory, reach for chunked reads (`pd.read_csv(..., chunksize=...)`), pyarrow-backed dtypes, or hand off to spark-jobs.\n\n### Step 2: inspect before touching\n\nRun `df.info()`, `df.describe()`, `df.isna().sum()`, and `df.nunique()` first. You are looking for: object columns that should be numeric or datetime, null counts, and cardinality. This is a two-minute sanity pass, not a full audit - for a genuinely unfamiliar dataset, run eda-playbook first.\n\n### Step 3: fix dtypes early\n\n- Parse dates with `pd.to_datetime`; pass `format=` when known (it is faster and catches malformed rows), `format=\"mixed\"` only when formats genuinely vary.\n- Coerce numerics with `pd.to_numeric(col, errors=\"coerce\")`, then **count and report** what failed: `col.isna().sum()`. Silent coercion is how \"N\u002FA\" strings become invisible revenue holes.\n- Cast low-cardinality strings to `category` when distinct values are under roughly 50% of row count - it cuts memory and speeds groupbys.\n- Downcast large numeric columns (`pd.to_numeric(col, downcast=\"integer\")`) when memory is tight.\n\n### Step 4: handle missing data deliberately\n\nChoose drop, fill, or flag - and state which and why in a comment. If more than ~5% of a key column is null, stop and ask whether the nulls are structural (a pipeline branch) before imputing; that determination changes the right treatment entirely.\n\n### Step 5: join with validation, never on faith\n\n- Always state `how=` and `on=` explicitly.\n- Always pass `validate=` (\"one_to_one\", \"many_to_one\", \"many_to_many\") so pandas raises on unexpected key duplication instead of silently fanning out.\n- After the join, assert the row count: a left join must preserve `len(left)`.\n- Check match rate with `indicator=True` when a low match rate would be a data problem.\n\n### Step 6: reshape and aggregate\n\n- `pivot_table` for long→wide, `melt` for wide→long.\n- `groupby().agg()` with **named aggregations** - `agg(revenue=(\"amount\", \"sum\"))` - so output columns are self-describing.\n- Remember `sum()` skips NaN silently; if a NaN-contaminated group should show as missing, use `min_count=1`.\n\n### Step 7: verify the output\n\nReconcile at least one aggregate against the pre-transformation source (total rows, total revenue). Show the transformation step by step with a comment on each step explaining intent, not mechanics.\n\n## Worked example\n\nMessy orders joined to a customer table with a duplicate key - the validation catches it:\n\n```python\nimport pandas as pd\n\norders = pd.DataFrame({\n    \"order_id\": [1001, 1002, 1003, 1004, 1005],\n    \"customer_id\": [\"C01\", \"C02\", \"C01\", \"C03\", \"C02\"],\n    \"amount\": [\"120.50\", \"80.00\", \"N\u002FA\", \"45.25\", \"200.00\"],\n})\ncustomers = pd.DataFrame({\n    \"customer_id\": [\"C01\", \"C02\", \"C03\", \"C03\"],   # C03 duplicated upstream\n    \"region\": [\"West\", \"East\", \"South\", \"South-dup\"],\n})\n\n# Coerce and count failures instead of letting them vanish\norders[\"amount\"] = pd.to_numeric(orders[\"amount\"], errors=\"coerce\")\nprint(orders[\"amount\"].isna().sum(), \"amounts failed to parse\")\n\n# validate= raises instead of silently duplicating order 1004\ntry:\n    orders.merge(customers, on=\"customer_id\", how=\"left\", validate=\"many_to_one\")\nexcept pd.errors.MergeError as e:\n    print(\"MergeError:\", str(e).splitlines()[0])\n\ncustomers = customers.drop_duplicates(\"customer_id\", keep=\"first\")\nmerged = orders.merge(customers, on=\"customer_id\", how=\"left\", validate=\"many_to_one\")\nassert len(merged) == len(orders)\n\nsummary = (\n    merged.groupby(\"region\", as_index=False)\n    .agg(orders=(\"order_id\", \"count\"), revenue=(\"amount\", \"sum\"))\n    .sort_values(\"revenue\", ascending=False)\n)\nprint(summary.to_string(index=False))\n```\n\nOutput:\n\n```\n1 amounts failed to parse\nMergeError: Merge keys are not unique in right dataset; not a many-to-one merge\nregion  orders  revenue\n  East       2   280.00\n  West       2   120.50\n South       1    45.25\n```\n\nRead it: the \"N\u002FA\" string was counted, not swallowed; the duplicate C03 row raised at the merge line instead of inflating South's revenue; West shows 2 orders but only 120.50 revenue because the failed parse became NaN and `sum` skipped it - a fact the failure count already disclosed.\n\n## Performance rules\n\n- Avoid `apply` over rows when a vectorized op, `np.where`, or `np.select` exists - vectorized versions are commonly 10-100x faster.\n- Avoid chained indexing (`df[a][b] = ...`); use `.loc[rows, cols]` for all assignment.\n- Watch for silent upcasts to `object` dtype after concatenation or fillna - they destroy both speed and memory.\n- Prefer `merge` over row-wise lookups; prefer `groupby().transform` over apply-per-group when the output is row-aligned.\n\n## Deliverable\n\nProduce a runnable, commented transformation script (or notebook cells) containing: the dtype-fixing block with coercion-failure counts, every join carrying `how=`, `on=`, `validate=`, and a row-count assertion, named aggregations, and a final reconciliation check against a number the user already trusts.\n\n## Do NOT\n\n- Do not merge without `validate=` - fan-out from duplicated keys is the single most common source of inflated metrics.\n- Do not use `errors=\"coerce\"` without counting what was coerced; a clean-looking column can hide hundreds of destroyed values.\n- Do not use `apply` with a lambda for arithmetic a vectorized expression can do.\n- Do not mutate the input DataFrame in place inside a function; copy when in doubt.\n- Do not trust `sum()` on a column with NaN to mean \"complete total\" - it means \"total of what parsed\".\n- Do not profile an unknown dataset with ad-hoc snippets; run eda-playbook and come back with its decision log.\n\n## Quality bar\n\nA finished transformation ships only when: every join is validated and row-count-asserted; every type coercion reports its failure count; at least one output aggregate reconciles to a trusted source number; no chained indexing or row-wise `apply` remains where a vectorized form exists; and a reader can follow the intent from comments without running the code.\n",{"name":5,"metadata":24,"description":7},{"title":25},"Pandas Expert","main","97a13607d669157a4a16d5ba79b77f208319f7b9","skills\u002Fpandas-expert",3,"https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills","https:\u002F\u002Fskillme.dev","https:\u002F\u002Fgithub.com\u002FSkillMedev","MCP-native catalog of Claude Agent Skills. Browse, recommend, install, and auto-apply skills in chat. Open source, every skill free and clonable on GitHub.","2026-10-14 03:19:31",{"source":8,"skillName":5,"commands":36,"prompt":46,"zipAvailable":47},[37,40,43],{"tool":38,"command":39},"npx","npx skills add SkillMedev\u002Fskills --skill pandas-expert",{"tool":41,"command":42},"bunx","bunx skills add SkillMedev\u002Fskills --skill pandas-expert",{"tool":44,"command":45},"pnpm","pnpm dlx skills add SkillMedev\u002Fskills --skill pandas-expert","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fpandas-expert\u002FSKILL.md",true,[],[50,58,65,72,79,86],{"skillNo":51,"name":52,"nameZh":6,"description":53,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":54,"categories":55,"keywords":56,"publishedAt":57},"01M75271B256DF3FX2VG5VRYEE","zone-2-cardio-plan","Use when someone asks to build a steady-state cardio plan, train an aerobic base or Zone 2, structure easy or endurance weekly cardio, train by heart-rate zones, or improve resting heart rate, VO2, or endurance. Builds a heart-rate-zone aerobic training block with session structure, modality choice, and progression. General fitness guidance, not medical advice. Do NOT use to design a strength or lifting program (use Strength Training Plan), high-intensity interval conditioning, HIIT, or circuits (use Conditioning & HIIT Program), or a mobility\u002Fflexibility routine (use Mobility Routine).","https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fzone-2-cardio-plan\u002FSKILL.md",[],[],"2026-11-11 18:27:18",{"skillNo":59,"name":60,"nameZh":6,"description":61,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":62,"categories":63,"keywords":64,"publishedAt":57},"01M7526VP1QBBKYBE17S1WR86Y","youtube-script-writer","Writes retention-engineered long-form YouTube scripts on a hook\u002Fbody\u002Fpayoff architecture - a first-30-seconds hook that survives the retention cliff, a beat-sheet body with mini-hooks and bridges, timed pattern interrupts, and a payoff that over-delivers - with B-roll and editing cues marked inline. Use when someone says \"script my next video\", \"my retention graph dies at the start\", \"tighten this draft\", or \"outline a video on X\". Do NOT use for the 3-second opener of TikTok, Reels, or Shorts - use video-hook-writer instead; do NOT use for spoken voiceover synced to on-screen tutorial actions - use narration-script instead; do NOT use for a sales demo built on a buyer's discovery notes - use demo-script instead; do NOT use to design the thumbnail and title - use thumbnail-concept instead.","https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube-script-writer\u002FSKILL.md",[],[],{"skillNo":66,"name":67,"nameZh":6,"description":68,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":69,"categories":70,"keywords":71,"publishedAt":57},"01M7526P2T0WKMHDXS4Q58237T","win-back-campaign","Designs segmented win-back email sequences for dormant users - dormancy tiers, a three-beat sequence with a single proportional incentive, and a suppression rule - and measures success by re-activation, not opens. Use when someone says \"our users went quiet\", \"design a win-back campaign\", \"how do I re-engage lapsed customers\", \"should we email people who haven't logged in for months\", or lifecycle messaging has stopped landing with a lapsed segment. Do NOT use for recovering an unfinished checkout - use abandoned-cart-sequence instead. Do NOT use for diagnosing and fixing why active customers cancel - use churn-reduction instead. Do NOT use for building always-on onboarding or nurture flows - use email-drip-builder instead.","https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fwin-back-campaign\u002FSKILL.md",[],[],{"skillNo":73,"name":74,"nameZh":6,"description":75,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":76,"categories":77,"keywords":78,"publishedAt":57},"01M7526HF2RCDJ50P93WRZRHVV","wikipedia-style","Writes neutral, verifiable, encyclopedic prose following Wikipedia's core content policies and manual of style - NPOV, inline citations, notability-backed claims, and a strict ban on promotional language. Use when someone asks \"write this like a Wikipedia article\", \"draft a Wikipedia page for this company or person\", \"make this neutral and encyclopedic\", or \"why does my article draft keep getting rejected\". Do NOT use for persuasive or opinion writing - use op-ed-writer instead; for thesis-driven academic papers, use academic-essay; for verifying claims in existing text, use fact-checker.","https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fwikipedia-style\u002FSKILL.md",[],[],{"skillNo":80,"name":81,"nameZh":6,"description":82,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":83,"categories":84,"keywords":85,"publishedAt":57},"01M7526D43CTGRKGKNQ2GB0KVX","whitepapers","Structures and writes authoritative B2B whitepapers - a standalone executive summary, a data-sized problem statement, methodology, evidence-driven findings, recommendations, and a soft call to action - flagging every claim that needs a real citation. Use when a marketer asks \"write a whitepaper on this topic\", \"turn our research into a lead-gen asset\", \"make this credible enough for executives\", or a draft reads like a product pitch instead of analysis. Do NOT use for customer-story-driven proof - use case-study-builder instead; for policy audiences, use policy-brief.","https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fwhitepapers\u002FSKILL.md",[],[],{"skillNo":87,"name":88,"nameZh":6,"description":89,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":90,"categories":91,"keywords":92,"publishedAt":57},"01M75268423Y3K50SY5WC2NX2G","white-space-analysis","Finds unmet market gaps by ranking jobs-to-be-done on importance versus satisfaction, overlaying them against current solutions and workarounds, and validating that each gap is real, winnable, and worth winning. Use when someone asks \"where are the gaps in this market\", \"what's underserved here\", \"where should we play next\", or is hunting for product, feature, or market-entry opportunities. Do NOT use for profiling competitors and building battle cards - use competitive-intelligence instead; for putting a dollar figure on an identified opportunity - use market-sizing instead; for extracting jobs-to-be-done from raw customer interviews - use jtbd-extractor instead.","https:\u002F\u002Fgithub.com\u002FSkillMedev\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fwhite-space-analysis\u002FSKILL.md",[],[]]