{"id":"cb754a2a-7e68-4db7-b84c-106846c6a032","shortId":"3EhWrp","kind":"skill","title":"product-update-logger","tagline":"Tell the skill what your product shipped. It writes a polished dated entry to a living docs/changelog.md and produces a ready-to-use content package: tweet thread, LinkedIn post, email snippet, and one-liner.","description":"# product-update-logger\n\nTell this skill what your product shipped. It writes a polished changelog entry to `docs/changelog.md` (a living log, newest entry first) and simultaneously produces a content package: tweet thread, LinkedIn post, email snippet, and one-liner.\n\nInput sources: free text from your message, git commits auto-read from the local repo, or GitHub PRs if you provide a repo. Any combination works.\n\n## Reference Files\n\nRead these files before each run:\n\n```bash\ncat references/changelog-format.md\ncat references/content-rules.md\ncat references/noise-filter.md\n```\n\n---\n\n## Step 1: Setup Check\n\n```bash\necho \"GITHUB_TOKEN: ${GITHUB_TOKEN:-not set -- GitHub PR fetching disabled}\"\necho \"Git:          $(git rev-parse --is-inside-work-tree 2>/dev/null && echo 'repo detected' || echo 'not a git repo')\"\necho \"Changelog:    $(ls docs/changelog.md 2>/dev/null && echo 'exists' || echo 'will be created')\"\n```\n\nNote whether git is available and whether a changelog already exists. This determines the version label format.\n\n---\n\n## Step 2: Parse Input\n\nCollect from the conversation:\n\n- `items` -- free text description of what shipped (pipe-separated if multiple). Optional if git is available.\n- `since` -- how many days back to look. Default: 7. User may say \"last 2 weeks\" (14) or \"since last release.\"\n- `repo` -- GitHub \"owner/repo\" for PR fetching. Optional.\n- `version_label` -- custom label like \"v2.1.0\" or \"The Speed Update.\" Optional; default is date-based.\n\n**If the user said nothing about items AND there is no git repo:** Ask \"What did you ship? List the features, fixes, or improvements -- one per line.\"\n\n**If git is available and user said nothing specific:** Proceed with git auto-read in Step 3. Show the user what was found and confirm before transforming.\n\nWrite parsed input:\n\n```bash\npython3 << 'PYEOF'\nimport json, os, re\n\ninp = {\n    \"items\": \"\",          # FILL: pipe-separated free text, or \"\" if none\n    \"since\": 7,           # FILL: integer days\n    \"repo\": \"\",           # FILL: \"owner/repo\" or \"\"\n    \"version_label\": \"\"   # FILL: \"\" means auto (date-based), or custom string\n}\n\nwith open(\"/tmp/pul-input.json\", \"w\") as f:\n    json.dump(inp, f, indent=2)\nprint(f\"Since: {inp['since']} days\")\nprint(f\"Free text items: {inp['items'] or 'none (will use git/GitHub)'}\")\nprint(f\"GitHub repo: {inp['repo'] or 'none'}\")\nprint(f\"Version label: {inp['version_label'] or 'auto (date-based)'}\")\nPYEOF\n```\n\n---\n\n## Step 3: Run the Gather Script\n\n```bash\nls scripts/gather.py 2>/dev/null && echo \"script found\" || echo \"ERROR: scripts/gather.py not found\"\n```\n\n```bash\nGITHUB_TOKEN=\"${GITHUB_TOKEN:-}\" python3 scripts/gather.py \\\n    --since \"$(python3 -c \"import json; print(json.load(open('/tmp/pul-input.json'))['since'])\")\" \\\n    --repo \"$(python3 -c \"import json; print(json.load(open('/tmp/pul-input.json'))['repo'])\")\" \\\n    --items \"$(python3 -c \"import json; print(json.load(open('/tmp/pul-input.json'))['items'])\")\" \\\n    --output /tmp/pul-raw.json\n```\n\nVerify output:\n\n```bash\npython3 -c \"\nimport json\nwith open('/tmp/pul-raw.json') as f:\n    d = json.load(f)\nprint(f'Items found:      {d[\\\"total_items\\\"]}')\nprint(f'Noise filtered:   {d[\\\"noise_filtered\\\"]}')\nprint(f'Git available:    {d[\\\"git_available\\\"]}')\nprint(f'GitHub available: {d[\\\"github_available\\\"]}')\nprint(f'Sources: git={sum(1 for i in d[\\\"items\\\"] if i[\\\"source\\\"]==\\\"git_commit\\\")}, '\n      f'prs={sum(1 for i in d[\\\"items\\\"] if i[\\\"source\\\"]==\\\"github_pr\\\")}, '\n      f'text={sum(1 for i in d[\\\"items\\\"] if i[\\\"source\\\"]==\\\"free_text\\\")}')\nprint()\nprint('Items:')\nfor item in d['items']:\n    print(f'  [{item[\\\"source\\\"]}] {item[\\\"subject\\\"]}')\n\"\n```\n\n**If total_items == 0:** Stop. Tell the user: \"No shipped items found. Either describe what you shipped, point me to a git repo with recent commits, or add a GitHub repo with `repo: owner/repo` and a GITHUB_TOKEN.\"\n\n**Show the item list to the user and ask: \"These are the items I found. Anything to add or remove before I write the changelog?\"**\n\nWait for confirmation or edits. If the user says \"looks good\", \"proceed\", or makes no changes, continue. If the user adds or removes items, update `/tmp/pul-raw.json` accordingly before Step 4.\n\n---\n\n## Step 4: Generate Changelog Entry\n\nPrint items for context:\n\n```bash\npython3 -c \"\nimport json\nwith open('/tmp/pul-raw.json') as f:\n    d = json.load(f)\nprint(json.dumps(d['items'], indent=2))\nprint()\nprint(f'Existing changelog format: {d[\\\"existing_changelog\\\"][\\\"format\\\"]}')\nprint(f'Last label: {d[\\\"existing_changelog\\\"][\\\"last_label\\\"]}')\nprint(f'Today: {d[\\\"date\\\"]}')\n\"\n```\n\n**AI instructions:** Transform each raw item from technical language to user-facing benefit language. Follow `references/changelog-format.md` for transformation rules and examples.\n\nRules:\n- **Do NOT invent outcomes or metrics.** \"40% faster\" must come from the source data. If no number is in the commit or PR, do not add one.\n- **Use past tense:** \"Added\", \"Fixed\", \"Improved\" -- not \"Adds\", \"Fixes\"\n- **Assign exactly one category** to each item: New, Improved, Fixed, or Under the hood\n- **Under the hood:** Only include if developer-relevant (API changes, breaking changes). Omit empty sections.\n- **Omit** anything that maps to: test changes, CI changes, documentation-only commits\n\nDetermine version label:\n- If user specified one: use it exactly\n- If `existing_changelog.format == \"semver\"`: increment based on changes (patch for fixes only, minor for any new feature)\n- Default: `Week of [Month Day, Year]` using today's date\n\nWrite the entry to `/tmp/pul-entry.json`:\n\n```json\n{\n  \"label\": \"Week of April 23, 2026\",\n  \"date\": \"2026-04-23\",\n  \"new\": [\n    {\"title\": \"Dark mode\", \"description\": \"Toggle in Settings > Appearance. Works across all views.\"}\n  ],\n  \"improved\": [\n    {\"title\": \"API response time\", \"description\": \"40% faster on average. Dashboard now loads in under 1 second.\"}\n  ],\n  \"fixed\": [\n    {\"title\": \"CSV export\", \"description\": \"Exports no longer drop the last row.\"}\n  ],\n  \"under_the_hood\": []\n}\n```\n\nVerify the entry:\n\n```bash\npython3 -c \"\nimport json\nwith open('/tmp/pul-entry.json') as f:\n    e = json.load(f)\nprint(f'Label: {e[\\\"label\\\"]}')\ntotal = 0\nfor cat in ['new', 'improved', 'fixed', 'under_the_hood']:\n    items = e.get(cat, [])\n    if items:\n        print(f'{cat.replace(\\\"_\\\", \\\" \\\").title()} ({len(items)}):')\n        for item in items:\n            print(f'  - {item[\\\"title\\\"]}: {item[\\\"description\\\"]}')\n        total += len(items)\nprint(f'Total: {total} items')\n\"\n```\n\n---\n\n## Step 5: Generate Content Package\n\nUsing the changelog entry from Step 4, generate all four content pieces. Follow `references/content-rules.md` strictly.\n\n**One-liner** (max 20 words): One sentence covering the biggest 1-2 items. Plain language, no jargon.\n\n**Tweet thread** (3-5 tweets):\n- Tweet 1: Hook -- \"We shipped [N] things this week.\" or lead with the biggest feature\n- Tweets 2-N: One item per tweet, 1-2 sentences max\n- Last tweet: \"Changelog: [link]\" or \"More next week.\" (optional)\n- Each tweet strictly under 280 characters\n- No hashtags. No em dashes. Active voice.\n\n**LinkedIn post**:\n- No markdown (asterisks render as literal on LinkedIn)\n- No hashtags\n- Founder voice: \"We shipped\", not \"We are excited to announce\"\n- Short paragraphs (1-2 sentences each), blank lines between them\n- Close with a question or observation, not a CTA\n- 150-400 words total\n\n**Email snippet**:\n- Subject: \"What shipped this week: [biggest item] + [1 more]\"\n- Body: 50-100 words. \"Here's what we shipped this week:\" then bullets.\n\nWrite to `/tmp/pul-content.json`:\n\n```json\n{\n  \"one_liner\": \"Dark mode, faster API, and a fixed export bug.\",\n  \"tweet_thread\": [\n    \"We shipped 3 things this week.\",\n    \"Dark mode is live. Toggle it in Settings > Appearance. Works everywhere.\",\n    \"API response time is now 40% faster. Dashboard loads in under a second.\",\n    \"Fixed: CSV exports were dropping the last row. That's gone now.\",\n    \"Changelog: [link]\"\n  ],\n  \"linkedin_post\": \"We shipped 3 updates this week.\\n\\nDark mode is live. Toggle it in Settings under Appearance. It works across every view.\\n\\nAPI response time is 40% faster on average. The dashboard now loads in under a second for most users.\\n\\nWe also fixed a bug where CSV exports were silently dropping the last row. If you hit this and stopped exporting, it's worth trying again.\\n\\nWhat feature have you been waiting for?\",\n  \"email_snippet\": {\n    \"subject\": \"What shipped this week: dark mode + faster API\",\n    \"body\": \"Here's what we shipped this week:\\n\\n- Dark mode: toggle in Settings > Appearance\\n- API response time: 40% faster, dashboard loads under 1 second\\n- Fixed: CSV exports no longer drop the last row\\n\\nFull changelog below.\"\n  }\n}\n```\n\n---\n\n## Step 6: Self-QA\n\n```bash\npython3 -c \"\nimport json, re\n\nwith open('/tmp/pul-raw.json') as f:\n    raw = json.load(f)\nwith open('/tmp/pul-entry.json') as f:\n    entry = json.load(f)\nwith open('/tmp/pul-content.json') as f:\n    content = json.load(f)\n\nfull_text = json.dumps(entry) + json.dumps(content)\nfails = 0\n\n# Check 1: No em dashes\nif chr(8212) in full_text:\n    print('FAIL: em dash found -- replace with hyphen')\n    fails += 1\nelse:\n    print('PASS: no em dashes')\n\n# Check 2: Banned words\nbanned = ['powerful', 'robust', 'seamless', 'innovative', 'game-changing',\n          'streamline', 'leverage', 'transform', 'revolutionize', 'excited to announce',\n          'pleased to announce', 'we are thrilled', 'cutting-edge', 'best-in-class',\n          'world-class', 'unlock', 'delightful']\nfound = [w for w in banned if w.lower() in full_text.lower()]\nif found:\n    print(f'FAIL: banned words found: {found}')\n    fails += 1\nelse:\n    print('PASS: no banned words')\n\n# Check 3: Tweet length\nthread = content.get('tweet_thread', [])\nlong_tweets = [(i+1, len(t)) for i, t in enumerate(thread) if len(t) > 280]\nif long_tweets:\n    print(f'FAIL: tweets over 280 chars: {long_tweets}')\n    fails += 1\nelse:\n    print(f'PASS: all {len(thread)} tweets under 280 chars')\n\n# Check 4: LinkedIn no hashtags\nli = content.get('linkedin_post', '')\nif re.search(r'#[A-Za-z]', li):\n    print('FAIL: hashtags found in LinkedIn post')\n    fails += 1\nelse:\n    print('PASS: no hashtags in LinkedIn')\n\n# Check 5: No markdown in LinkedIn\nif '**' in li or '__' in li:\n    print('FAIL: markdown formatting in LinkedIn (renders as literal asterisks)')\n    fails += 1\nelse:\n    print('PASS: no markdown in LinkedIn')\n\n# Check 6: One-liner word count\none_liner = content.get('one_liner', '')\nword_count = len(one_liner.split())\nif word_count > 20:\n    print(f'FAIL: one-liner is {word_count} words (max 20)')\n    fails += 1\nelse:\n    print(f'PASS: one-liner is {word_count} words')\n\n# Check 7: Item count\nentry_items = (len(entry.get('new', [])) + len(entry.get('improved', [])) +\n               len(entry.get('fixed', [])) + len(entry.get('under_the_hood', [])))\nraw_total = raw['total_items']\nprint(f'INFO: {entry_items} changelog items from {raw_total} raw items')\n\nprint()\nprint(f'Result: {\\\"PASS\\\" if fails == 0 else f\\\"FAIL ({fails} issues)\\\"}')\n\"\n```\n\n**If any check fails:** Fix the issue in the relevant temp file before proceeding to Step 7. Re-run the check after fixing.\n\n---\n\n## Step 7: Append to Changelog + Save Content\n\n```bash\npython3 << 'PYEOF'\nimport json, os, re\n\nwith open('/tmp/pul-entry.json') as f:\n    entry = json.load(f)\nwith open('/tmp/pul-content.json') as f:\n    content = json.load(f)\n\n# Build the new changelog section\nlines = [f\"## {entry['label']}\", \"\"]\n\nCAT_HEADERS = {\n    \"new\": \"### New\",\n    \"improved\": \"### Improved\",\n    \"fixed\": \"### Fixed\",\n    \"under_the_hood\": \"### Under the hood\",\n}\n\nfor cat, header in CAT_HEADERS.items():\n    items = entry.get(cat, [])\n    if items:\n        lines.append(header)\n        for item in items:\n            lines.append(f\"- **{item['title']}** -- {item['description']}\")\n        lines.append(\"\")\n\nlines.append(\"---\")\nlines.append(\"\")\nnew_section = \"\\n\".join(lines)\n\n# Prepend to docs/changelog.md\nos.makedirs(\"docs\", exist_ok=True)\nchangelog_path = \"docs/changelog.md\"\n\nif os.path.exists(changelog_path):\n    existing = open(changelog_path).read()\n    # Insert after the top-level heading (if any) or at the very top\n    if existing.startswith(\"# \"):\n        end_of_heading = existing.index(\"\\n\") + 1\n        updated = existing[:end_of_heading] + \"\\n\" + new_section + existing[end_of_heading:]\n    else:\n        updated = new_section + existing\nelse:\n    updated = \"# Changelog\\n\\n\" + new_section\n\nwith open(changelog_path, \"w\") as f:\n    f.write(updated)\n\nprint(f\"Changelog updated: {changelog_path}\")\n\n# Save content package\ndate = entry['date']\ncontent_dir = \"docs/product-updates\"\nos.makedirs(content_dir, exist_ok=True)\ncontent_path = f\"{content_dir}/{date}-content.md\"\n\ncontent_lines = [\n    f\"# Content Package: {entry['label']}\",\n    \"\",\n    \"## One-liner\",\n    content.get('one_liner', ''),\n    \"\",\n    \"## Tweet Thread\",\n    \"\",\n]\nthread = content.get('tweet_thread', [])\nfor i, tweet in enumerate(thread, 1):\n    content_lines.append(f\"[{i}/{len(thread)}] {tweet}\")\n    content_lines.append(\"\")\n\ncontent_lines += [\n    \"## LinkedIn Post\",\n    \"\",\n    content.get('linkedin_post', ''),\n    \"\",\n    \"## Email Snippet\",\n    \"\",\n    f\"Subject: {content.get('email_snippet', {}).get('subject', '')}\",\n    \"\",\n    content.get('email_snippet', {}).get('body', ''),\n    \"\",\n]\n\nwith open(content_path, \"w\") as f:\n    f.write(\"\\n\".join(content_lines))\n\nprint(f\"Content package: {content_path}\")\nPYEOF\n```\n\n---\n\n## Step 8: Clean Up and Present\n\n```bash\nrm -f /tmp/pul-input.json /tmp/pul-raw.json /tmp/pul-entry.json /tmp/pul-content.json\necho \"Done.\"\n```\n\nPresent to the user in this order:\n\n**1. Changelog entry** (formatted markdown, not raw JSON):\n\n```\n## Week of April 23, 2026\n\n### New\n- **Dark mode** -- Toggle in Settings > Appearance. Works across all views.\n\n### Improved\n- **API response time** -- 40% faster on average. Dashboard now loads in under 1 second.\n\n### Fixed\n- **CSV export** -- Exports no longer drop the last row.\n```\n\n**2. Content package:**\n\n- One-liner: `[text]`\n- Tweet thread: numbered list of tweets\n- LinkedIn post: full text\n- Email snippet: subject line + body\n\n**3. Saved files:**\n- `docs/changelog.md` -- updated (new entry prepended)\n- `docs/product-updates/[date]-content.md` -- full content package saved\n\n---\n\n## Common Mistakes\n\n| The agent will want to... | Why that's wrong |\n|---|---|\n| Invent outcomes or metrics | Every claim must come from the raw items. \"40% faster\" needs to come from the commit message or PR body. If no number is present, don't add one. |\n| Write \"We are excited to announce\" | Banned. Use \"We shipped\", \"[Feature] is now live\", or just state the fact. |\n| Use markdown bold (**) in LinkedIn | LinkedIn renders ** as literal asterisks. Plain text only. |\n| Add hashtags to LinkedIn or tweets | This skill never uses hashtags. |\n| Put all items in \"New\" | Bugs are Fixed, speed improvements are Improved. Miscategorizing weakens the changelog. |\n| Skip the confirmation step in Step 3 | Always show the item list and ask the user to confirm before transforming. This prevents wrong-branch commits or stale items. |\n| Include empty \"Under the hood\" section | Omit if empty. Silence is better than noise. |\n| Combine multiple items into one tweet | One item per tweet. Specificity > breadth. |\n| Pad with filler tweets | If there's one real item, write 2 tweets. Don't pad to 5. |","tags":["product","update","logger","opendirectory","varnan-tech","agent-skills","gtm","hermes-agent","openclaw-skills","skills","technical-seo"],"capabilities":["skill","source-varnan-tech","skill-product-update-logger","topic-agent-skills","topic-gtm","topic-hermes-agent","topic-openclaw-skills","topic-skills","topic-technical-seo"],"categories":["opendirectory"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/Varnan-Tech/opendirectory/product-update-logger","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add Varnan-Tech/opendirectory","source_repo":"https://github.com/Varnan-Tech/opendirectory","install_from":"skills.sh"}},"qualityScore":"0.502","qualityRationale":"deterministic score 0.50 from registry signals: · indexed on github topic:agent-skills · 104 github stars · SKILL.md body (15,458 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-24T12:55:44.494Z","embedding":null,"createdAt":"2026-04-23T18:56:43.745Z","updatedAt":"2026-04-24T12:55:44.494Z","lastSeenAt":"2026-04-24T12:55:44.494Z","tsv":"'+1':1465 '-04':860 '-100':1117 '-2':1000,1034,1084 '-23':861 '-400':1101 '-5':1009 '/dev/null':152,166,414 '/tmp/pul-content.json':1130,1349,1710,1957 '/tmp/pul-entry.json':850,917,1341,1702,1956 '/tmp/pul-input.json':356,438,448,458,1954 '/tmp/pul-raw.json':461,471,651,672,1333,1955 '0':566,929,1362,1656 '1':125,510,524,538,890,999,1012,1033,1083,1113,1304,1364,1383,1447,1491,1528,1559,1600,1810,1897,1967,2004 '14':230 '150':1100 '2':151,165,191,228,364,413,683,1027,1391,2016,2222 '20':992,1586,1598 '2026':857,859,1979 '23':856,1978 '280':1050,1477,1486,1501 '3':302,405,1008,1147,1193,1455,2038,2162 '4':655,657,979,1504 '40':737,881,1167,1218,1299,1995,2076 '5':969,1537,2228 '50':1116 '6':1321,1568 '7':223,335,1613,1678,1687 '8':1946 '8212':1370 'a-za-z':1515 'accord':652 'across':872,1210,1988 'activ':1057 'ad':761 'add':590,618,646,756,765,2095,2129 'agent':2056 'ai':708 'alreadi':182 'also':1235 'alway':2163 'announc':1080,1408,1411,2102 'anyth':616,798 'api':790,877,1137,1162,1278,1296,1992 'appear':870,1159,1207,1294,1986 'append':1688 'april':855,1977 'ask':271,609,2169 'assign':767 'asterisk':1063,1557,2125 'auto':92,298,347,399 'auto-read':91,297 'avail':177,214,288,494,497,501,504 'averag':884,1221,1998 'back':219 'ban':1392,1394,1432,1442,1452,2103 'base':257,350,402,824 'bash':117,128,316,410,423,464,665,910,1325,1693,1951 'benefit':721 'best':1419 'best-in-class':1418 'better':2196 'biggest':998,1024,1111 'blank':1087 'bodi':1115,1279,1925,2037,2087 'bold':2118 'branch':2180 'breadth':2210 'break':792 'bug':1142,1238,2145 'build':1716 'bullet':1127 'c':432,442,452,466,667,912,1327 'cat':118,120,122,931,941,1725,1740,1746 'cat.replace':946 'cat_headers.items':1743 'categori':770 'chang':641,791,793,803,805,826,1401 'changelog':56,162,181,625,659,688,692,700,975,1039,1187,1318,1642,1690,1719,1777,1782,1786,1830,1837,1846,1848,1968,2155 'char':1487,1502 'charact':1051 'check':127,1363,1390,1454,1503,1536,1567,1612,1664,1683 'chr':1369 'ci':804 'claim':2069 'class':1421,1424 'clean':1947 'close':1091 'collect':194 'combin':107,2199 'come':740,2071,2080 'commit':90,520,588,751,809,2083,2181 'common':2053 'confirm':310,628,2158,2173 'content':29,70,971,983,1352,1360,1692,1713,1851,1856,1860,1865,1868,1872,1875,1905,1928,1936,1940,1942,2017,2050 'content.get':1459,1509,1576,1882,1888,1909,1916,1921 'content.md':1871,2048 'content_lines.append':1898,1904 'context':664 'continu':642 'convers':197 'count':1573,1580,1585,1595,1610,1615 'cover':996 'creat':172 'csv':894,1176,1240,1308,2007 'cta':1099 'custom':244,352 'cut':1416 'cutting-edg':1415 'd':474,481,488,495,502,514,528,542,555,675,680,690,698,706 'dark':864,1134,1151,1275,1289,1981 'dash':1056,1367,1377,1389 'dashboard':885,1169,1223,1301,1999 'data':744 'date':16,256,349,401,707,845,858,1853,1855,1870,2047 'date-bas':255,348,400 'day':218,338,370,840 'default':222,253,836 'delight':1426 'describ':576 'descript':201,866,880,896,959,1760 'detect':155 'determin':185,810 'develop':788 'developer-relev':787 'dir':1857,1861,1869 'disabl':139 'doc':1773 'docs/changelog.md':21,59,164,1771,1779,2041 'docs/product-updates':1858,2046 'document':807 'documentation-on':806 'done':1959 'drop':900,1179,1244,1312,2012 'e':920,926 'e.get':940 'echo':129,140,153,156,161,167,169,415,418,1958 'edg':1417 'edit':630 'either':575 'els':1384,1448,1492,1529,1560,1601,1657,1823,1828 'em':1055,1366,1376,1388 'email':35,76,1104,1268,1912,1917,1922,2033 'empti':795,2186,2193 'end':1805,1813,1820 'entri':17,57,64,660,848,909,976,1344,1358,1616,1640,1705,1723,1854,1877,1969,2044 'entry.get':1619,1622,1625,1628,1745 'enumer':1472,1895 'error':419 'everi':1211,2068 'everywher':1161 'exact':768,819 'exampl':729 'excit':1078,1406,2100 'exist':168,183,687,691,699,1774,1784,1812,1819,1827,1862 'existing.index':1808 'existing.startswith':1804 'existing_changelog.format':821 'export':895,897,1141,1177,1241,1254,1309,2008,2009 'f':359,362,366,372,384,392,473,476,478,485,492,499,506,521,535,558,674,677,686,695,704,919,922,924,945,955,964,1335,1338,1343,1346,1351,1354,1440,1482,1494,1588,1603,1638,1651,1658,1704,1707,1712,1715,1722,1756,1841,1845,1867,1874,1899,1914,1932,1939,1953 'f.write':1842,1933 'face':720 'fact':2115 'fail':1361,1375,1382,1441,1446,1483,1490,1521,1527,1549,1558,1589,1599,1655,1659,1660,1665 'faster':738,882,1136,1168,1219,1277,1300,1996,2077 'featur':278,835,1025,1262,2107 'fetch':138,240 'file':110,113,1673,2040 'fill':325,336,340,345 'filler':2213 'filter':487,490 'first':65 'fix':279,762,766,776,829,892,935,1140,1175,1236,1307,1626,1666,1685,1731,1732,2006,2147 'follow':723,985 'format':189,689,693,1551,1970 'found':308,417,422,480,574,615,1378,1427,1438,1444,1445,1523 'founder':1071 'four':982 'free':84,199,329,373,547 'full':1355,1372,2031,2049 'full_text.lower':1436 'game':1400 'game-chang':1399 'gather':408 'generat':658,970,980 'get':1919,1924 'git':89,141,142,159,175,212,269,286,296,493,496,508,519,584 'git/github':382 'github':99,130,132,136,236,385,424,426,500,503,533,592,599 'gone':1185 'good':636 'hashtag':1053,1070,1507,1522,1533,2130,2139 'head':1795,1807,1815,1822 'header':1726,1741,1750 'hit':1250 'hood':780,783,906,938,1631,1735,1738,2189 'hook':1013 'hyphen':1381 'import':319,433,443,453,467,668,913,1328,1696 'improv':281,763,775,875,934,1623,1729,1730,1991,2149,2151 'includ':785,2185 'increment':823 'indent':363,682 'info':1639 'innov':1398 'inp':323,361,368,376,387,395 'input':82,193,315 'insert':1789 'insid':148 'instruct':709 'integ':337 'invent':733,2064 'is-inside-work-tre':146 'issu':1661,1668 'item':198,264,324,375,377,450,459,479,483,515,529,543,551,553,556,559,561,565,573,603,613,649,662,681,713,773,939,943,949,951,953,956,958,962,967,1001,1030,1112,1614,1617,1636,1641,1643,1648,1744,1748,1752,1754,1757,1759,2075,2142,2166,2184,2201,2206,2220 'jargon':1005 'join':1767,1935 'json':320,434,444,454,468,669,851,914,1131,1329,1697,1974 'json.dump':360 'json.dumps':679,1357,1359 'json.load':436,446,456,475,676,921,1337,1345,1353,1706,1714 'label':188,243,245,344,394,397,697,702,812,852,925,927,1724,1878 'languag':716,722,1003 'last':227,233,696,701,902,1037,1181,1246,1314,2014 'lead':1021 'len':948,961,1466,1475,1497,1581,1618,1621,1624,1627,1901 'length':1457 'level':1794 'leverag':1403 'li':1508,1519,1544,1547 'like':246 'line':284,1088,1721,1768,1873,1906,1937,2036 'liner':40,81,990,1133,1571,1575,1578,1592,1607,1881,1884,2021 'lines.append':1749,1755,1761,1762,1763 'link':1040,1188 'linkedin':33,74,1059,1068,1189,1505,1510,1525,1535,1541,1553,1566,1907,1910,2029,2120,2121,2132 'list':276,604,2026,2167 'liter':1066,1556,2124 'live':20,61,1154,1201,2110 'load':887,1170,1225,1302,2001 'local':96 'log':62 'logger':4,44 'long':1462,1479,1488 'longer':899,1311,2011 'look':221,635 'ls':163,411 'make':639 'mani':217 'map':800 'markdown':1062,1539,1550,1564,1971,2117 'max':991,1036,1597 'may':225 'mean':346 'messag':88,2084 'metric':736,2067 'minor':831 'miscategor':2152 'mistak':2054 'mode':865,1135,1152,1199,1276,1290,1982 'month':839 'multipl':209,2200 'must':739,2070 'n':1016,1028,1197,1213,1233,1260,1287,1288,1295,1306,1316,1766,1809,1816,1831,1832,1934 'napi':1214 'ndark':1198 'need':2078 'never':2137 'new':774,834,862,933,1620,1718,1727,1728,1764,1817,1825,1833,1980,2043,2144 'newest':63 'next':1043 'nfull':1317 'nois':486,489,2198 'none':333,379,390 'note':173 'noth':262,292 'number':747,2025,2090 'nwe':1234 'nwhat':1261 'observ':1096 'ok':1775,1863 'omit':794,797,2191 'one':39,80,282,757,769,816,989,994,1029,1132,1570,1574,1577,1591,1606,1880,1883,2020,2096,2203,2205,2218 'one-lin':38,79,988,1569,1590,1605,1879,2019 'one_liner.split':1582 'open':355,437,447,457,470,671,916,1332,1340,1348,1701,1709,1785,1836,1927 'option':210,241,252,1045 'order':1966 'os':321,1698 'os.makedirs':1772,1859 'os.path.exists':1781 'outcom':734,2065 'output':460,463 'owner/repo':237,341,596 'packag':30,71,972,1852,1876,1941,2018,2051 'pad':2211,2226 'paragraph':1082 'pars':145,192,314 'pass':1386,1450,1495,1531,1562,1604,1653 'past':759 'patch':827 'path':1778,1783,1787,1838,1849,1866,1929,1943 'per':283,1031,2207 'piec':984 'pipe':206,327 'pipe-separ':205,326 'plain':1002,2126 'pleas':1409 'point':580 'polish':15,55 'post':34,75,1060,1190,1511,1526,1908,1911,2030 'power':1395 'pr':137,239,534,753,2086 'prepend':1769,2045 'present':1950,1960,2092 'prevent':2177 'print':365,371,383,391,435,445,455,477,484,491,498,505,549,550,557,661,678,684,685,694,703,923,944,954,963,1374,1385,1439,1449,1481,1493,1520,1530,1548,1561,1587,1602,1637,1649,1650,1844,1938 'proceed':294,637,1675 'produc':23,68 'product':2,10,42,50 'product-update-logg':1,41 'provid':103 'prs':100,522 'put':2140 'pyeof':318,403,1695,1944 'python3':317,428,431,441,451,465,666,911,1326,1694 'qa':1324 'question':1094 'r':1514 'raw':712,1336,1632,1634,1645,1647,1973,2074 're':322,1330,1680,1699 're-run':1679 're.search':1513 'read':93,111,299,1788 'readi':26 'ready-to-us':25 'real':2219 'recent':587 'refer':109 'references/changelog-format.md':119,724 'references/content-rules.md':121,986 'references/noise-filter.md':123 'releas':234 'relev':789,1671 'remov':620,648 'render':1064,1554,2122 'replac':1379 'repo':97,105,154,160,235,270,339,386,388,440,449,585,593,595 'respons':878,1163,1215,1297,1993 'result':1652 'rev':144 'rev-pars':143 'revolution':1405 'rm':1952 'robust':1396 'row':903,1182,1247,1315,2015 'rule':727,730 'run':116,406,1681 'said':261,291 'save':1691,1850,2039,2052 'say':226,634 'script':409,416 'scripts/gather.py':412,420,429 'seamless':1397 'second':891,1174,1229,1305,2005 'section':796,1720,1765,1818,1826,1834,2190 'self':1323 'self-qa':1322 'semver':822 'sentenc':995,1035,1085 'separ':207,328 'set':135,869,1158,1205,1293,1985 'setup':126 'ship':11,51,204,275,572,579,1015,1074,1108,1123,1146,1192,1272,1284,2106 'short':1081 'show':303,601,2164 'silenc':2194 'silent':1243 'simultan':67 'sinc':215,232,334,367,369,430,439 'skill':7,47,2136 'skill-product-update-logger' 'skip':2156 'snippet':36,77,1105,1269,1913,1918,1923,2034 'sourc':83,507,518,532,546,560,743 'source-varnan-tech' 'specif':293,2209 'specifi':815 'speed':250,2148 'stale':2183 'state':2113 'step':124,190,301,404,654,656,968,978,1320,1677,1686,1945,2159,2161 'stop':567,1253 'streamlin':1402 'strict':987,1048 'string':353 'subject':562,1106,1270,1915,1920,2035 'sum':509,523,537 'technic':715 'tell':5,45,568 'temp':1672 'tens':760 'test':802 'text':85,200,330,374,536,548,1356,1373,2022,2032,2127 'thing':1017,1148 'thread':32,73,1007,1144,1458,1461,1473,1498,1886,1887,1890,1896,1902,2024 'thrill':1414 'time':879,1164,1216,1298,1994 'titl':863,876,893,947,957,1758 'today':705,843 'toggl':867,1155,1202,1291,1983 'token':131,133,425,427,600 'top':1793,1802 'top-level':1792 'topic-agent-skills' 'topic-gtm' 'topic-hermes-agent' 'topic-openclaw-skills' 'topic-skills' 'topic-technical-seo' 'total':482,564,928,960,965,966,1103,1633,1635,1646 'transform':312,710,726,1404,2175 'tree':150 'tri':1258 'true':1776,1864 'tweet':31,72,1006,1010,1011,1026,1032,1038,1047,1143,1456,1460,1463,1480,1484,1489,1499,1885,1889,1893,1903,2023,2028,2134,2204,2208,2214,2223 'unlock':1425 'updat':3,43,251,650,1194,1811,1824,1829,1843,1847,2042 'use':28,381,758,817,842,973,2104,2116,2138 'user':224,260,290,305,570,607,633,645,719,814,1232,1963,2171 'user-fac':718 'v2.1.0':247 'verifi':462,907 'version':187,242,343,393,396,811 'view':874,1212,1990 'voic':1058,1072 'w':357,1428,1430,1839,1930 'w.lower':1434 'wait':626,1266 'want':2058 'weaken':2153 'week':229,837,853,1019,1044,1110,1125,1150,1196,1274,1286,1975 'whether':174,179 'word':993,1102,1118,1393,1443,1453,1572,1579,1584,1594,1596,1609,1611 'work':108,149,871,1160,1209,1987 'world':1423 'world-class':1422 'worth':1257 'write':13,53,313,623,846,1128,2097,2221 'wrong':2063,2179 'wrong-branch':2178 'year':841 'z':1518 'za':1517","prices":[{"id":"5a4c5926-3f4c-4724-a7c0-107660fcea4c","listingId":"cb754a2a-7e68-4db7-b84c-106846c6a032","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"Varnan-Tech","category":"opendirectory","install_from":"skills.sh"},"createdAt":"2026-04-23T18:56:43.745Z"}],"sources":[{"listingId":"cb754a2a-7e68-4db7-b84c-106846c6a032","source":"github","sourceId":"Varnan-Tech/opendirectory/product-update-logger","sourceUrl":"https://github.com/Varnan-Tech/opendirectory/tree/main/skills/product-update-logger","isPrimary":false,"firstSeenAt":"2026-04-23T18:56:43.745Z","lastSeenAt":"2026-04-24T12:55:44.494Z"}],"details":{"listingId":"cb754a2a-7e68-4db7-b84c-106846c6a032","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"Varnan-Tech","slug":"product-update-logger","github":{"repo":"Varnan-Tech/opendirectory","stars":104,"topics":["agent-skills","gtm","hermes-agent","openclaw-skills","skills","technical-seo"],"license":null,"html_url":"https://github.com/Varnan-Tech/opendirectory","pushed_at":"2026-04-24T10:35:19Z","description":" AI Agent Skills built for GTM, Technical Marketing, and growth automation.","skill_md_sha":"3a95dea394376ef50a14afb7fed54cfe129234dc","skill_md_path":"skills/product-update-logger/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/Varnan-Tech/opendirectory/tree/main/skills/product-update-logger"},"layout":"multi","source":"github","category":"opendirectory","frontmatter":{"name":"product-update-logger","description":"Tell the skill what your product shipped. It writes a polished dated entry to a living docs/changelog.md and produces a ready-to-use content package: tweet thread, LinkedIn post, email snippet, and one-liner."},"skills_sh_url":"https://skills.sh/Varnan-Tech/opendirectory/product-update-logger"},"updatedAt":"2026-04-24T12:55:44.494Z"}}