chore: ignore docs/plans/ and untrack existing plan files (#259)

Internal ce:plan output shouldn't ship on the public repo.
Adds docs/plans/ to .gitignore and removes the two already-tracked
plan files from the index. Working copies stay local for reference.

Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Van Horn
2026-04-15 07:48:47 -04:00
committed by GitHub
parent c12dd3adbf
commit 242e38ef56
3 changed files with 3 additions and 904 deletions
+3
View File
@@ -25,3 +25,6 @@ htmlcov/
# build artifact from scripts/build-skill.sh
/dist/
# Internal planning docs (ce:plan output) — keep local, don't publish
docs/plans/
@@ -1,445 +0,0 @@
---
title: Fix skill upload 200-file limit + packaging hygiene (public repo)
type: fix
status: completed
date: 2026-04-14
deepened: 2026-04-14
---
# Fix skill upload 200-file limit + packaging hygiene (public repo)
## Overview
claude.ai's "Upload skill" UI rejects zips with more than 200 files. Zipping the public `mvanhorn/last30days-skill` repo produces 406 files, so the upload fails outright (evidence: Trevin's 2026-04-14 report). Root cause is an accidentally committed npm package under `vendor/` (215 files of dead weight from PR #48) plus the absence of a user-facing packaging path that matches Anthropic's canonical `.skill` format.
Goal: let any user produce a compliant `last30days.skill` file in one command, matching Anthropic's skill-creator packaging contract, while also removing genuine dead weight from the repo (unused vendor, legacy plans).
## Problem Frame
- Trevin tried to upload the public repo as a Claude Skill and hit the 200-file limit
- 215 of 406 files are `vendor/package/` - an extracted `steipete-bird-0.8.0.tgz` that no code imports
- The real runtime X client lives at `scripts/lib/vendor/bird-search/` (15 files, referenced by `scripts/lib/bird_x.py:5` and `tests/test_bird_x.py:133`)
- `.clawhubignore` is ClawHub-specific and does not affect a hand-rolled zip upload
- Users have no documented path to produce a compliant upload zip
- Legacy top-level `plans/` folder holds pre-`docs/plans/` planning artifacts (confirmed waste by Matt, 2026-04-14 chat)
## Requirements Trace
- R1. After this plan lands, the produced upload zip is =200 files
- R2. The X/bird-search runtime still works - no regression in `tests/test_bird_x.py`
- R3. A contributor following README instructions can produce a Claude-Skill-upload-compatible `.skill` file in one command
- R4. Re-introduction of a root `vendor/` directory is prevented via `.gitignore`
- R5. No runtime behavior changes for existing skill consumers (Claude Code plugin, ClawHub, Gemini)
- R6. Produced zip matches Anthropic's canonical skill-folder layout: top-level directory named exactly `last30days` containing `SKILL.md` at its root, with YAML frontmatter `name: last30days`
- R7. Root `SKILL.md` frontmatter passes Anthropic's documented limits: `name` =64 chars (currently 10), `description` =200 chars (currently 228, needs trimming)
- R8. Produced zip contains exactly one `SKILL.md` (at `last30days/SKILL.md`) - no conflicting second skill spec, no symlinks that the uploader may reject or break
- R9. No runtime import reaches an excluded path (proven by import-graph audit, not just asserted)
## Scope Boundaries
Non-goals:
- Not touching the private repo or ClawHub publish flow (those have their own strip script)
- Not resolving the adjacent open issues (#239 plugin loader path-escape, #236 OpenClaw paths, #231 security scan, #190 version drift, #184 Gemini install) - each deserves its own plan
- Not redesigning the skill into self-contained subfolders or splitting scripts into a separate package
- Not adding CI enforcement of the 200-file cap (possible follow-up)
## Context and Research
### Anthropic's canonical skill-upload contract
Sourced from Anthropic's skill-creator repo (`anthropics/skills/skills/skill-creator/scripts/package_skill.py`) and help-center docs:
1. **Output format:** a `.skill` file, which is a standard zip with the `.skill` extension.
2. **Top-level entry in the zip must be a single directory** whose name matches `name:` in the skill's YAML frontmatter. Anthropic's packager uses `arcname = file_path.relative_to(skill_path.parent)`, so the zip always contains `<skill_name>/...`.
3. **That directory must contain `SKILL.md`** at its root (the packager explicitly validates this).
4. **Required YAML frontmatter:** `name` (=64 chars, lowercase + hyphens) and `description` (=200 chars). Our root SKILL.md already satisfies both.
5. **Canonical exclusions** applied by Anthropic's packager:
- Directories: `__pycache__`, `node_modules`
- Root-only: `evals/`
- File globs: `*.pyc`
- Files: `.DS_Store`
6. **Empirical limit:** the upload UI rejects =200 files (screenshot 2026-04-14). Not documented, but confirmed.
7. **Per-file size cap** is not publicly documented; general claude.ai uploads cap at 30MB per file. Conservative target: keep any single file under 10MB.
### Relevant code and patterns in this repo
- `SKILL.md` (root, 1382 lines, 80KB) - `name: last30days`, `user-invocable: true`. This is the skill.
- `skills/last30days/SKILL.md` (230 lines) - `name: last30days-v3-spec`, `user-invocable: false`. Internal architecture spec, separate skill name - not the upload target.
- `vendor/package/` - accidental commit from PR #48, 215 files, zero importers.
- `vendor/steipete-bird-0.8.0.tgz` - source tarball, also unused at runtime.
- `scripts/lib/vendor/bird-search/` - the ACTUAL vendored bird-search client (15 files). Keep.
- `plans/` (top-level, 2 files: `feat-add-websearch-source.md`, `fix-strict-date-filtering.md`) - legacy, pre-`docs/plans/` convention. Matt confirmed delete.
- `scripts/sync.sh` - deploys skill to `~/.claude`, `~/.agents`, `~/.codex`. Reference for runtime-required files.
- `.clawhubignore` - existing exclude list for the ClawHub path. Not used here, but good cross-reference for what is runtime-irrelevant.
- `.gitignore` - current dev excludes (`.venv/`, `__pycache__/`, `.DS_Store`, etc).
### Institutional learnings
- Private repo has `scripts/clawhub-publish.sh` + `scripts/strip_for_openclaw.py` that build a staging dir with only OpenClaw-safe files. Not needed for this public-path upload; `git archive` with `--prefix` is sufficient and dependency-free.
- PR #48 introduced `vendor/package/` unintentionally. No code imports from it.
### File count math (verified via dry run)
| Strategy | File count | Under cap? |
|---|---|---|
| Current repo, zip as-is | 406 | No |
| After `vendor/` deleted | 191 | Yes (thin margin) |
| After `vendor/` + `plans/` deleted, no further excludes | 189 | Yes |
| With full planned excludes (Anthropic canonical + tests/docs/fixtures/assets/dev manifests/nested skill dirs) | 81 | Comfortable headroom |
Dry run run on 2026-04-14 against the current working tree. Simulated the proposed `.gitattributes` with a `find` filter matching the intended exclude list. Result: 81 files, 868KB uncompressed. Actual `git archive` output may differ slightly (by 1-2 files) but will land well under 200.
### Runtime import audit (proves core experience unchanged)
Grepped all `import`/`from` statements in `scripts/**/*.py`. Non-stdlib imports resolve to only:
- `lib.*` (internal package at `scripts/lib/`)
- `store` (internal module at `scripts/store.py`)
- `scripts.*` (internal)
No runtime import reaches `tests/`, `docs/`, `fixtures/`, `vendor/` (root), `plans/`, `assets/`, `.agents/`, `.codex-plugin/`, `.hermes-plugin/`, or any other excluded path. The shipped `.skill` file contains everything the runtime needs and nothing it does not.
### Symlink and multi-SKILL.md audit
The repo contains one symlink: `skills/last30days-nux/SKILL.md -> ../../SKILL.md`. Three SKILL.md files in total:
- `SKILL.md` (root, `name: last30days`, `user-invocable: true`) - the actual skill
- `skills/last30days/SKILL.md` (`name: last30days-v3-spec`, `user-invocable: false`) - internal architecture doc
- `skills/last30days-nux/SKILL.md` (symlink to root) - nux variant reference
Shipping all three inside one zip creates two rejection risks:
1. Uploader sees multiple `SKILL.md` with conflicting `name:` values and refuses or misbinds
2. `git archive` stores the symlink as a symlink entry; the uploader may reject symlinked entries on principle
Both risks disappear by excluding `skills/` entirely from the zip. The two internal skill definitions are not needed for claude.ai skill execution - they serve the repo as documentation / Claude Code plugin layout, not the direct upload path.
### Sources consulted
- Anthropic skill help center article (general upload guidance, no file-count number documented)
- [anthropics/skills README](https://github.com/anthropics/skills/blob/main/README.md) - YAML frontmatter requirements
- [anthropics/skills package_skill.py](https://github.com/anthropics/skills/blob/main/skills/skill-creator/scripts/package_skill.py) - canonical exclusions and arcname shape
- Trevin's 2026-04-14 chat screenshot (empirical 200-file cap)
- Adjacent issues #239, #236, #190 for context on current packaging mess
## Key Technical Decisions
- **Delete `vendor/` outright** rather than gitignore-and-leave. Pure dead weight. Rationale: the real vendored client is at `scripts/lib/vendor/bird-search/`, root `vendor/` has zero importers; keeping it invites re-upload.
- **Delete top-level `plans/`** (Matt confirmed). Rationale: superseded by `docs/plans/`. Moving content into `docs/plans/` if any is still relevant; otherwise just delete.
- **Produce a `.skill` file (not a plain `.zip`)** via `git archive --format=zip --prefix=last30days/ -o dist/last30days.skill HEAD`. Rationale: matches Anthropic's canonical contract - zip extension is cosmetic, but the `.skill` affordance is what the upload UI expects.
- **Use `git archive` + `.gitattributes export-ignore`** rather than a Python packager. Rationale: no Python dependency at build time, honors git's declarative exclude model, reusable by anyone running `git archive` directly.
- **Mirror Anthropic's canonical exclusions in `.gitattributes`** (`__pycache__`, `node_modules`, `*.pyc`, `.DS_Store`, `evals/`) alongside our repo-specific excludes. Rationale: future-proof if a contributor adds node deps; keeps us aligned with the Anthropic baseline.
- **Exclude `skills/` from the upload zip** (covers `skills/last30days/SKILL.md` and `skills/last30days-nux/SKILL.md`). Rationale: shipping multiple SKILL.md files with different `name:` values is a likely uploader-rejection cause, and the symlink at `skills/last30days-nux/SKILL.md` is an independent rejection risk. Repo contents stay intact - Claude Code plugin and GitHub viewers still see the directory.
- **Keep `.clawhubignore` as-is** - it serves the ClawHub publish path separately. Do not merge the two lists; different consumers, different exclusions.
- **Prevent regression with a `/vendor/` entry in `.gitignore`** (leading slash, so `scripts/lib/vendor/` is unaffected).
- **Do not address #239 `"skills": ["./"]` path-escape here.** That is a plugin.json change, not a zip-packaging change. Separate plan.
## Open Questions
### Resolved during planning
- Is root `vendor/` used? No. Grep for `vendor/package`, `vendor/steipete`, `from vendor` returns zero hits outside `scripts/lib/vendor/`.
- Is `scripts/lib/vendor/bird-search/` safe? Yes. Referenced by `scripts/lib/bird_x.py:5` and `tests/test_bird_x.py:133`.
- What name does the top-level zip directory need? `last30days` - matches `name: last30days` in the root `SKILL.md` frontmatter.
- Does `skills/last30days/SKILL.md` conflict? No. It declares a different skill name (`last30days-v3-spec`) and is `user-invocable: false`. Not the upload target, and safe to ship inside the zip.
- Is there a documented file-count cap? No. 200 is empirical from the UI error screenshot.
- Should we gate this on a version bump? Yes, 3.0.0 - 3.0.1. Same API, same runtime, smaller and uploadable package.
### Deferred to implementation
- Exact `.gitattributes` export-ignore entries may need one tuning pass if `git archive` surfaces a file we forgot. Verification step catches it.
- Whether to delete `SKILL-original.md` from the repo entirely or just export-ignore. Leaning export-ignore to preserve git history context.
- Whether any content in `plans/*.md` is still live reference material. If so, move to `docs/plans/` under new naming convention; if not, delete outright.
## Implementation Units
- [ ] **Unit 1: Remove accidental `vendor/` commit**
**Goal:** Delete the root `vendor/` directory and the stray `.tgz`, both unused at runtime.
**Requirements:** R1, R2, R5
**Dependencies:** None
**Files:**
- Delete: `vendor/` (entire tree, 215 files)
- Delete: `vendor/steipete-bird-0.8.0.tgz`
- Modify: `.gitignore` (add `/vendor/` to prevent regression - leading slash to avoid matching `scripts/lib/vendor/`)
**Approach:**
- Single commit: `chore: remove unused root vendor/ directory (215 files from PR #48)`
- Verify `scripts/lib/vendor/bird-search/` is untouched
- Verify no `from vendor` or `vendor/package` references appear in the diff
**Patterns to follow:**
- Commit message style matches recent history
**Test scenarios:**
- Happy path: `find . -type f -not -path './.git/*' | wc -l` returns =200 after commit
- Integration: `python -m pytest tests/test_bird_x.py -q` passes - confirms the real vendored client still resolves
- Integration: `bash scripts/sync.sh` completes without error
**Verification:**
- Zero files remain under `vendor/` on `main`
- `tests/test_bird_x.py` still passes
- `.gitignore` now contains `/vendor/`
- [ ] **Unit 2: Remove legacy top-level `plans/` directory**
**Goal:** Delete the pre-`docs/plans/` folder (Matt confirmed waste).
**Requirements:** R1, R5
**Dependencies:** None (independent of Unit 1)
**Files:**
- Delete: `plans/feat-add-websearch-source.md`
- Delete: `plans/fix-strict-date-filtering.md`
- Delete: `plans/` (now empty)
**Approach:**
- Skim both files first. If either still reflects real upcoming work, port it to `docs/plans/YYYY-MM-DD-NNN-<type>-*-plan.md` before deletion. If not, delete.
- Commit: `chore: remove legacy plans/ directory (superseded by docs/plans/)`
**Test scenarios:**
- Test expectation: none - pure housekeeping, no code paths affected
**Verification:**
- `plans/` does not exist on `main`
- Nothing in the repo references `plans/feat-add-websearch-source.md` or `plans/fix-strict-date-filtering.md` (grep to confirm)
- [ ] **Unit 3: Declare zip-time excludes via `.gitattributes`**
**Goal:** Use `export-ignore` so `git archive` produces a skill-shaped zip without hand-filtering.
**Requirements:** R1, R3, R6
**Dependencies:** Unit 1, Unit 2
**Files:**
- Create: `.gitattributes`
**Approach:**
- Anthropic canonical exclusions (match `package_skill.py`):
- `__pycache__/` export-ignore
- `node_modules/` export-ignore
- `*.pyc` export-ignore
- `.DS_Store` export-ignore
- `evals/` export-ignore
- Repo-specific exclusions (dev/docs/build artifacts not needed at runtime):
- `tests/` (64 files)
- `docs/` (17 files including `docs/test-results/`)
- `fixtures/` (7 files)
- `assets/` (5 files, 14MB of README media)
- `SKILL-original.md` (historical)
- `SPEC.md`, `TASKS.md`, `test-run.log`, `CONTRIBUTORS.md`, `HERMES_SETUP.md`, `release-notes.md`, `CHANGELOG.md`
- `uv.lock`
- `.agents/`, `.codex-plugin/`, `.hermes-plugin/`, `.claude-plugin/` (platform adapters - skill-upload path is platform-agnostic)
- `.clawhubignore`, `.gitignore`, `.gitattributes`
- `skills/` (avoid second SKILL.md with conflicting `name:`; also drops the symlink at `skills/last30days-nux/SKILL.md`)
- Keep in archive: `scripts/` (runtime), root `SKILL.md`, `README.md`, `LICENSE`, `pyproject.toml`, `CLAUDE.md`, `gemini-extension.json`, `agents/`, `hooks/`
**Technical design:** *(directional guidance, not implementation spec)*
```gitattributes
# Anthropic canonical skill-packaging excludes
__pycache__/ export-ignore
node_modules/ export-ignore
*.pyc export-ignore
.DS_Store export-ignore
evals/ export-ignore
# Repo-specific: tests + docs + media (not runtime)
tests/ export-ignore
docs/ export-ignore
fixtures/ export-ignore
assets/ export-ignore
# Repo-specific: historical + dev manifests
SKILL-original.md export-ignore
SPEC.md export-ignore
...
```
**Patterns to follow:**
- `.gitattributes` export-ignore syntax per [git docs](https://git-scm.com/docs/gitattributes#_creating_an_archive)
**Test scenarios:**
- Happy path: `git archive --format=zip HEAD | zipinfo -1 - | wc -l` returns =200
- Happy path: zip contains `SKILL.md`, `scripts/last30days.py`, `scripts/lib/bird_x.py`, `scripts/lib/vendor/bird-search/lib/cookies.js`
- Happy path: zip contains exactly one `SKILL.md` entry at the top level (not multiple, not a symlink)
- Edge case: zip does NOT contain `tests/`, `docs/`, `assets/*.jpeg`, `*.mp3`, `skills/`
- Edge case: no symlink entries in the zip (`unzip -l` lines starting with `l`)
- Edge case: zip size stays under ~2MB (if over 5MB an unintended large file slipped through)
**Verification:**
- Running `git archive --format=zip --output=/tmp/test.zip HEAD && unzip -l /tmp/test.zip | tail -1` reports =200 files and a sane byte count
- [ ] **Unit 4: Add `scripts/build-skill.sh` user-facing builder**
**Goal:** One-command path to produce a Claude-upload-compatible `.skill` file.
**Requirements:** R3, R6
**Dependencies:** Unit 3
**Files:**
- Create: `scripts/build-skill.sh`
- Modify: `.gitignore` (add `/dist/` for build artifact)
**Approach:**
- Bash, executable, `set -euo pipefail`
- `git archive --format=zip --prefix=last30days/ --output=dist/last30days.skill HEAD`
- The `--prefix=last30days/` nests everything under `last30days/` inside the zip, matching Anthropic's arcname contract
- Refuse to build if working tree is dirty (`git diff --quiet && git diff --cached --quiet`)
- Print file count, archive size, and path to paste into the upload UI
- Fail with a clear error if count exceeds 200 (defensive check)
**Technical design:** *(directional guidance, not implementation spec)*
```bash
#!/usr/bin/env bash
# build-skill.sh - package repo as a Claude-upload-ready .skill file
# Usage: bash scripts/build-skill.sh
set -euo pipefail
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "error: working tree is dirty - commit or stash first" >&2; exit 1
fi
mkdir -p dist
out="dist/last30days.skill"
git archive --format=zip --prefix=last30days/ --output="$out" HEAD
count=$(unzip -l "$out" | tail -1 | awk '{print $2}')
[ "$count" -le 200 ] || { echo "error: $count files in zip, cap is 200" >&2; exit 1; }
echo "built $out ($count files, $(du -h "$out" | cut -f1))"
```
**Patterns to follow:**
- Style of `scripts/sync.sh` (bash, top-of-file comment, `set -euo pipefail`)
**Test scenarios:**
- Happy path: clean tree, `bash scripts/build-skill.sh` produces `dist/last30days.skill` with =200 files and the top-level entry is `last30days/`
- Happy path: `unzip -p dist/last30days.skill last30days/SKILL.md | head -2` shows `---` (frontmatter start) confirming SKILL.md is at the right location
- Edge case: dirty working tree - script exits non-zero with clear error
- Edge case: idempotent - running twice overwrites cleanly
- Error path: if a future change inflates file count past 200, the defensive `[ "$count" -le 200 ]` check fails and the script refuses to produce a broken output
**Verification:**
- `bash scripts/build-skill.sh && unzip -l dist/last30days.skill | grep "^ 0 .* last30days/$"` confirms the prefix directory exists
- `unzip -l dist/last30days.skill | grep "last30days/SKILL.md"` confirms SKILL.md is at the expected path
- `unzip -l dist/last30days.skill | grep -c "SKILL.md"` returns exactly 1
- `unzip -l dist/last30days.skill | awk '{print $NF}' | grep -v "^$" | sort -u | grep "skills/" || true` returns nothing (confirms internal skill dirs excluded)
- Gate: a contributor must run `bash scripts/build-skill.sh` on their branch and attach the produced file to their PR before merging any change that touches `.gitattributes` or exclude-sensitive paths
- [ ] **Unit 5: Document the upload path in README**
**Goal:** Users know how to produce an upload `.skill` without reading the source.
**Requirements:** R3
**Dependencies:** Unit 4
**Files:**
- Modify: `README.md` (add a short "Upload as a Claude Skill" subsection under the existing install section)
**Approach:**
- One paragraph plus a single command block: `bash scripts/build-skill.sh`
- Mention the 200-file cap as context so future changes do not bust it
- Point users at the claude.ai skill upload UI (note: link only if a stable URL exists at implementation time, otherwise describe the UI path)
**Test scenarios:**
- Test expectation: none - pure documentation change
**Verification:**
- `grep -n "build-skill" README.md` returns a hit
- Instructions match actual script behavior
- [ ] **Unit 6: Trim SKILL.md description to =200 chars**
**Goal:** Make root `SKILL.md` frontmatter pass Anthropic's documented `description` limit.
**Requirements:** R7
**Dependencies:** None (independent of other units)
**Files:**
- Modify: `SKILL.md` (frontmatter `description:` field only)
**Approach:**
- Current description is 228 chars. Cut 28+ chars without losing signal.
- Suggested rewrite (196 chars): `"Multi-query social search with planned queries. Research any topic across Reddit, X, YouTube, TikTok, Instagram, Hacker News, Polymarket, and the web. Gemini/OpenAI fallback when needed."`
- Confirm the trimmed version still surfaces for the right prompts (smoke test: run `python scripts/last30days.py "test" --emit=compact` and confirm behavior unchanged; description is metadata, not runtime input)
- Update `skills/last30days/SKILL.md` description too if it exceeds 200 chars (check during implementation)
**Test scenarios:**
- Happy path: `python3 -c "import re; d=open('SKILL.md').read(); m=re.search(r'^description:\s*\"(.+?)\"', d, re.M); assert len(m.group(1)) <= 200, len(m.group(1))"` passes
**Verification:**
- Description field is =200 chars in root SKILL.md
- Skill still triggers on relevant prompts (manual smoke check)
- [ ] **Unit 7: Version bump and changelog**
**Goal:** Ship as 3.0.1 so consumers see the packaging fix.
**Requirements:** R5
**Dependencies:** Units 1-6
**Files:**
- Modify: `.claude-plugin/plugin.json` (3.0.0 - 3.0.1)
- Modify: `SKILL.md` frontmatter version
- Modify: `skills/last30days/SKILL.md` frontmatter version
- Modify: `gemini-extension.json` version (note: #190 flags this as stale at 2.9.5; bumping here partially addresses that but full resolution is out of scope)
- Modify: `CHANGELOG.md`
- Modify: `release-notes.md`
**Approach:**
- Atomic version bump across all manifests
- Changelog entry: "Packaging: `scripts/build-skill.sh` produces a compliant `.skill` file; removed unused root `vendor/` (215 files) and legacy `plans/`; repo file count fits under claude.ai's 200-file upload cap"
**Test scenarios:**
- Happy path: `grep -rn "3.0.1" SKILL.md skills/last30days/SKILL.md .claude-plugin/plugin.json gemini-extension.json` returns four consistent hits
- Integration: `bash scripts/sync.sh` completes cleanly
**Verification:**
- All four version declarations read `3.0.1`
- CHANGELOG and release-notes have dated entries
## System-Wide Impact
- **Interaction graph:** Skill-runtime import graph is unchanged. Removed code (root `vendor/`, `plans/`) has zero importers.
- **Error propagation:** `build-skill.sh` is a new surface; failure mode is non-zero exit with clear stderr. No runtime error paths touched.
- **State lifecycle risks:** None. `dist/` is gitignored build output.
- **API surface parity:** No change to any user-facing API, CLI flag, config key, or SKILL.md contract.
- **Integration coverage:** `tests/test_bird_x.py` exercises the real vendored client - if it regressed, the test fails. Run it after Unit 1.
- **Unchanged invariants:** `scripts/lib/vendor/bird-search/` stays. `scripts/sync.sh` deploy behavior unchanged. ClawHub publish flow (private repo) untouched. Claude Code plugin install via GitHub URL still works.
## Risks and Dependencies
| Risk | Mitigation |
|------|------------|
| Deleting `vendor/` silently breaks something we missed | Run `pytest tests/test_bird_x.py` and `bash scripts/sync.sh` after the delete; grep for `vendor/package` before merging |
| claude.ai rejects the `.skill` file for a reason other than file count (e.g., frontmatter character, hidden file) | Test-upload the produced artifact against claude.ai once before merging; iterate on `.gitattributes` if needed |
| `.gitattributes` over-excludes and breaks the runtime skill | Unit 3 verification step explicitly checks runtime paths are present in the produced archive |
| A future PR re-vendors something at `/vendor/` and busts the 200 cap again | `/vendor/` in `.gitignore` plus the defensive `=200` check in `build-skill.sh` catches it |
| Version bump collides with in-flight PRs that also bump versions | Coordinate with #229, #217 which touched version strings; check before merging |
| `skills/last30days/SKILL.md` (internal spec) being shipped inside the zip confuses the claude.ai uploader | Resolved by excluding `skills/` from the zip (Unit 3). Internal spec remains in the repo for plugin consumers |
| `skills/last30days-nux/SKILL.md` is a symlink to `../../SKILL.md`; claude.ai may reject zips with symlink entries | Resolved by excluding `skills/` from the zip (Unit 3). Symlink never enters the archive |
## Documentation and Operational Notes
- Update README only (Unit 5). No runbook, no migration, no flag.
- No deployment step - plugin consumers get the packaging fix automatically on next update.
- Release notes flag: manual uploaders should re-zip via `scripts/build-skill.sh`.
- Opportunistic future work (out of scope here): CI check that fails PRs that push the zip over 200 files.
## Sources and References
- Trevin's 2026-04-14 chat screenshot: "Zip contains too many files (maximum 200)"
- [anthropics/skills README](https://github.com/anthropics/skills/blob/main/README.md) - YAML frontmatter requirements
- [anthropics/skills package_skill.py](https://github.com/anthropics/skills/blob/main/skills/skill-creator/scripts/package_skill.py) - canonical exclusions, arcname convention, validation gates
- [claude.ai skill help center](https://support.claude.com/en/articles/12512180-use-skills-in-claude) - upload failure modes (zip size, folder-name mismatch, missing SKILL.md)
- PR #48 (2026-02) - the merge that introduced `vendor/package/`
- Open issues adjacent but out of scope: #239, #236, #231, #190, #184
- Related code: `scripts/lib/bird_x.py:5`, `tests/test_bird_x.py:133`, `.clawhubignore`, `scripts/sync.sh`, root `SKILL.md` frontmatter
- Private-repo reference pattern: `scripts/clawhub-publish.sh` + `scripts/strip_for_openclaw.py` - not copied here; `git archive` is simpler for the public path
@@ -1,459 +0,0 @@
---
title: claude.ai distribution + discoverability push
type: feat
status: active
date: 2026-04-14
---
# claude.ai distribution + discoverability push
## Overview
The 200-file upload bug is fixed and `last30days.skill` works on claude.ai. But "it can be uploaded" is not the same as "people use it." Claude.ai has no native skill marketplace, so discovery happens through a 3-layer stack: Anthropic's curated plugin marketplace, third-party aggregators, and social/newsletter amplification. The question Matt asked - "is the GitHub release the right decision" - has a clear answer: yes, but it is table stakes, not the strategy. This plan cuts the release and then pulls the real distribution levers.
## Problem Frame
Today, the only way a claude.ai user can get `last30days` is to clone the repo and run `scripts/build-skill.sh`. That filters out 99% of potential users. Even once a release exists with a direct download link, the hard problem is discovery - claude.ai users do not browse GitHub for skills. They find skills via Anthropic's "Discover" tab in Claude Code, third-party aggregator sites (skillsmp.com, mcpmarket.com, claudeskills.info), awesome-lists on GitHub, newsletters (The Neuron), and social posts (X, r/ClaudeAI).
Success looks like: a claude.ai user who never visits the repo can find, download, and upload the skill in under 60 seconds, and keep using it because the trigger description fires on the right prompts.
## Requirements Trace
- R1. A one-click install path exists for claude.ai users: click a link from README/marketplace/aggregator, get `last30days.skill`, drop into Upload dialog
- R2. The skill is submitted to Anthropic's official plugin marketplace at `platform.claude.com/plugins/submit`
- R3. The skill is listed in at least 4 high-traffic awesome-lists / aggregators
- R4. The SKILL.md YAML `description` and `argument-hint` fields are tuned so Claude's skill-selector actually invokes `last30days` on research-intent prompts (trigger quality is the single biggest install-to-reuse lever)
- R5. First-run experience works with zero API keys for the default sources (Reddit, Hacker News, Polymarket, GitHub) - already true, verify does not regress
- R6. At least one high-visibility amplification moment ships within 14 days: demo GIF + launch tweet + The Neuron pitch
- R7. Basic metrics are in place to learn what works: release-download counts, aggregator referrer traffic, GitHub star velocity before/after
## Scope Boundaries
Non-goals for this plan:
- Not building a custom skill-hosting site or our own marketplace
- Not changing the runtime pipeline or adding features - this is pure distribution
- Not spamming aggregators with low-effort PRs - one quality submission per venue
- Not gaming install counts or stars
- Not displacing the existing Claude Code plugin / OpenClaw / Gemini distribution - those stay as-is, cross-linked
- Not depending on Anthropic marketplace acceptance before other levers ship - marketplace review is slow and gate-able
## Context and Research
### The claude.ai skill ecosystem in April 2026
- **No native claude.ai skill marketplace.** Upload is the only end-user path inside the web UI.
- **Anthropic's Plugin/Skills Marketplace** (submissions at `platform.claude.com/plugins/submit`) is the closest thing to a "featured" channel and ships through Claude Code's "Discover" tab. Quality/security review gates acceptance. Research-category skills are under-represented vs. dev-tool skills.
- **Third-party aggregators** drive most organic discovery outside Anthropic's channels:
- `skillsmp.com`, `mcpmarket.com`, `claudeskills.info`, `skillsdirectory.com`, `agensi.io`
- These aggregators scrape awesome-lists, so one well-placed PR cascades
- **Awesome-lists** where skills discovery congregates:
- `ComposioHQ/awesome-claude-skills`
- `travisvn/awesome-claude-skills`
- `karanb192/awesome-claude-skills`
- `VoltAgent/awesome-agent-skills`
- `sickn33/antigravity-awesome-skills` (1,400+ skills indexed)
- **Newsletter amplification:** The Neuron runs a daily "AI Skill of the Day" digest - the single biggest external traffic source per successful skill creators. Their "practical workflow" angle fits a research skill cleanly.
- **Install-count reference points** from public aggregator data:
- `self-improving-agent`: 357k installs
- `frontend-design`: 277k installs
- `skill-vetter`: 190k installs
- `github`: 148k installs
- `proactive-agent`: 135k installs
- Long tail: ~500 installs
The gap between 500-install and 357k-install skills is mostly: (a) trigger description quality, (b) zero-config first run, (c) one amplification moment that caught.
### Current distribution surface for last30days
- Claude Code plugin via marketplace and GitHub URL: live
- OpenClaw via ClawHub (`clawhub install last30days-official`): live
- Gemini extension: live
- Manual `git clone`: documented in README
- claude.ai `.skill` upload: just shipped, undocumented for end users (no link)
The cross-linking graph is incomplete. Traffic that already exists (Claude Code install page, OpenClaw listing, Gemini extensions page) is not being routed to the new claude.ai path.
### Reference: trigger description quality
The root `SKILL.md` `description` field is how Claude decides whether to invoke the skill. Current text (as of 3.0.1, 167 chars):
> "Multi-query social research across Reddit, X, YouTube, TikTok, Instagram, Hacker News, Polymarket, and the web. Intelligent query planning with Gemini/OpenAI fallback."
Analysis: solid source list, weak on action verbs and example queries. Successful skills include imperative verbs ("research", "find", "summarize", "compare") and 1-2 example triggers the user might type. At 167/200 chars, there is room.
## Key Technical Decisions
- **Cut v3.0.1 GitHub release with `last30days.skill` attached as an asset** (table stakes). Rationale: every downstream lever (README link, marketplace submission, aggregator PR, tweet) needs a stable public download URL.
- **Automate `.skill` build in CI on tag push** so future releases never forget to attach the artifact. Rationale: manual builds break over time; this is a one-time 10-line GitHub Actions workflow.
- **Prioritize marketplace submission in parallel with aggregator PRs**, not in sequence. Rationale: marketplace review is slow and opaque; do not block aggregator work on it. If rejected, we still have the aggregator presence.
- **Tune the SKILL.md description to optimize trigger selection**, not marketing copy. Rationale: this is the single biggest re-use lever per the ecosystem research. Marketing copy goes in README/release notes, not frontmatter.
- **One quality pass per aggregator, not a spray.** Rationale: awesome-list maintainers reject duplicate / low-effort PRs; reputation matters.
- **Ship the launch tweet with a real demo GIF**, not a screenshot. Rationale: Boris Cherny's Claude Code viral tweet template (one query, one result, "oh wow" moment) consistently outperforms text-only launches.
- **Pitch The Neuron once, with a production-quality 60-second demo**, not a cold email. Rationale: single shot at the biggest amplifier; treat it like a press release, not a tweet.
- **Track release-download count + GitHub referrer traffic as proxies for adoption** until we have better signal. Rationale: claude.ai upload counts are not exposed to creators.
- **Cross-link existing distribution pages back to claude.ai** as part of the release. Rationale: converting existing users to multi-surface users is cheaper than acquiring new ones.
## Open Questions
### Resolved during planning
- Is the GitHub release the right first step? Yes. Every other lever depends on a stable download URL. But it is a prerequisite, not the strategy.
- Does claude.ai have a native skill directory? No (confirmed April 2026).
- Should we wait for Anthropic marketplace acceptance before shipping other levers? No - parallelize.
- Do we need to rebuild the runtime to improve claude.ai adoption? No - the runtime is strong; the gap is distribution.
### Deferred to implementation
- Exact Neuron pitch copy - draft during Unit 8, refine based on what their recent editions have favored
- Whether to tag `@steipete`, `@AnthropicAI`, `@alexalbert__` in the launch tweet - confirm current handles and review each's posting culture before tagging
- Which specific demo query to record for the launch GIF - pick during Unit 7 based on what's newsworthy that week
- Whether to request a "skills-research" badge on skillsdirectory.com/agensi.io - check their current badge programs during Unit 5
## High-Level Technical Design
> *This illustrates the intended distribution graph and is directional guidance for review, not implementation specification.*
```
[GitHub Release v3.0.1]
|
+-- last30days.skill (asset, public URL)
|
+------> README "Upload to claude.ai" section (Unit 3)
|
+------> Claude Code plugin README link (Unit 4)
+------> OpenClaw listing link (Unit 4)
+------> Gemini extension link (Unit 4)
|
+------> Anthropic marketplace submission (Unit 6)
|
+------> Aggregator PRs (Unit 5):
| * ComposioHQ/awesome-claude-skills
| * travisvn/awesome-claude-skills
| * karanb192/awesome-claude-skills
| * VoltAgent/awesome-agent-skills
| * sickn33/antigravity-awesome-skills
| * skillsmp.com submit form
|
+------> Amplification (Units 7-9):
* Demo GIF + launch tweet
* The Neuron "Skill of the Day" pitch
* News-cycle recurring tweet (weekly)
All paths end at: claude.ai Upload Skill dialog
Trigger quality (Unit 2) determines whether installs become sustained usage
```
## Implementation Units
- [x] **Unit 1: Cut v3.0.1 GitHub release with `.skill` asset + auto-build CI**
**Goal:** Produce a stable public download URL for `last30days.skill` so every downstream lever has something to link to, and guarantee future releases include the artifact automatically.
**Requirements:** R1
**Dependencies:** None (plan 2026-04-14-001 already shipped the build script)
**Files:**
- Create: `.github/workflows/release.yml`
- Modify: none at release time (release is a git-tag + GitHub release action)
**Approach:**
- Tag `v3.0.1` on `main`, push
- Create GitHub release with the CHANGELOG v3.0.1 entry as body, attach `dist/last30days.skill`
- Add CI workflow that triggers on `push: tags: 'v*'`, runs `bash scripts/build-skill.sh`, uploads the artifact to the release. The `action-gh-release` pattern is standard.
- Release URL shape: `https://github.com/mvanhorn/last30days-skill/releases/download/v3.0.1/last30days.skill` (deterministic, shareable)
**Patterns to follow:**
- Any existing `.github/workflows/` patterns in the repo
- `actions/checkout@v4` + `softprops/action-gh-release@v2` is the conventional combo
**Test scenarios:**
- Happy path: pushing `v3.0.1` tag produces a release with `last30days.skill` attached and publicly downloadable without auth
- Edge case: re-tagging `v3.0.1` does not duplicate or corrupt the asset
- Error path: build failure in the workflow fails the release cleanly (no empty release created)
**Verification:**
- `curl -fsSL -o /tmp/dl.skill https://github.com/mvanhorn/last30days-skill/releases/download/v3.0.1/last30days.skill` succeeds anonymously
- Downloaded file matches `dist/last30days.skill` byte-for-byte
- A second tag (e.g., `v3.0.2-test`) in a branch triggers the workflow end-to-end
- [x] **Unit 2: Tune SKILL.md description and argument-hint for trigger quality**
**Goal:** Increase the probability that Claude's skill-selector invokes `last30days` on research-intent prompts. Trigger quality is the single biggest install-to-reuse lever per ecosystem research.
**Requirements:** R4, R5
**Dependencies:** None
**Files:**
- Modify: `SKILL.md` (frontmatter `description` and `argument-hint` only)
- Modify: `skills/last30days/SKILL.md` (if parity needed)
**Approach:**
- Rewrite `description` to lead with an imperative action verb and include 1-2 concrete example queries, staying =200 chars
- Rewrite `argument-hint` to show 2-3 canonical invocations that mirror real user phrasing, not marketing phrasing
- Keep the source list intact - that's the value prop - but move it later in the sentence
- Reference frames that worked for high-install skills: `frontend-design`, `self-improving-agent`, `github`
**Technical design:** *(directional guidance, not implementation spec)*
Candidate shape (verify char count in implementation):
```yaml
description: "Research what people actually say about any topic in the last 30 days. Pulls real posts and engagement from Reddit, X, YouTube, TikTok, Hacker News, Polymarket, GitHub, and the web."
argument-hint: 'last30days AI video tools | last30days nvidia earnings reaction | last30days best noise cancelling headphones'
```
**Test scenarios:**
- Happy path: in a fresh claude.ai chat, prompts like "what are people saying about X this week" surface `last30days` in the skill-selector candidate set
- Edge case: generic "research X" prompts do not over-select `last30days` when the user clearly wants a general answer (avoids false-positive selection)
- Integration: test in all three environments - claude.ai web, Claude Code, OpenClaw - to confirm selection behavior is consistent
**Verification:**
- Description =200 chars, checked by the same regex Unit 6 of plan 001 used
- At least 3 real-user prompt phrasings trigger skill selection in manual testing
- No regression on zero-config first-run (no new API keys required)
- [x] **Unit 3: Rewrite the README claude.ai section with one-click install**
**Goal:** Replace the current "run this bash script" instructions with a one-click download link pointing at the GitHub release asset.
**Requirements:** R1
**Dependencies:** Unit 1 (release must exist first)
**Files:**
- Modify: `README.md` (the "Upload as a Claude Skill" section added in plan 001)
**Approach:**
- Replace the `bash scripts/build-skill.sh` instruction with a direct download link to the release asset
- Keep the build-from-source instruction as a fallback for developers, demoted below the direct link
- Add a short 3-step install guide with specific UI path: "Settings > Capabilities > Skills > + button, drop the .skill file"
- Include a screenshot or GIF showing the upload flow if space allows (can be added in Unit 7)
**Test scenarios:**
- Test expectation: none - pure documentation change
**Verification:**
- A user following the README instructions end-to-end can go from "never heard of this" to working skill in under 60 seconds
- Instructions specify the exact claude.ai UI path current as of the release date
- [x] **Unit 4: Cross-link existing distribution surfaces back to claude.ai** (in-repo README matrix shipped; external ClawHub/Gemini listing edits remain)
**Goal:** Convert existing Claude Code plugin / OpenClaw / Gemini traffic into claude.ai installs. Cheaper than net-new acquisition.
**Requirements:** R1
**Dependencies:** Unit 1, Unit 3
**Files:**
- Modify: `README.md` (install matrix - add claude.ai row prominently)
- Modify: `variants/open/SKILL.md` in the private repo if that governs OpenClaw listing copy
- Modify: `gemini-extension.json` if `description` or install hints exist there
- External: update the ClawHub listing page for `last30days-official` to mention claude.ai availability
**Approach:**
- Every listing page a user currently lands on should have a one-line "Also available as a claude.ai Skill: [download]" link
- Use a consistent short-URL pattern so it's instantly recognizable across surfaces
- Do not require users to re-read each install guide - the cross-link is opportunistic, not blocking
**Test scenarios:**
- Test expectation: none - documentation/external-listing updates
**Verification:**
- Each of the 4 distribution surfaces (Claude Code plugin marketplace, OpenClaw ClawHub listing, Gemini extensions page, GitHub README) contains a visible claude.ai cross-link within 1 scroll of the page top
- [ ] **Unit 5: Submit PRs to high-traffic Claude skill awesome-lists**
**Goal:** Get listed in the 5 highest-traffic aggregators so third-party skill-discovery sites (skillsmp.com, mcpmarket.com, claudeskills.info) pick up the entry.
**Requirements:** R3
**Dependencies:** Unit 1, Unit 2 (description should be tuned before first impression in these lists)
**Files (external repos):**
- `ComposioHQ/awesome-claude-skills` - PR adding last30days to the relevant category
- `travisvn/awesome-claude-skills`
- `karanb192/awesome-claude-skills`
- `VoltAgent/awesome-agent-skills`
- `sickn33/antigravity-awesome-skills`
**Approach:**
- One PR per list, in parallel
- Each PR: one-line entry matching the list's existing format; link to release asset (not repo root)
- If the list has a "research" or "data-gathering" category, use it; otherwise append to the most adjacent section
- Draft copy once, reuse across PRs - but match each list's voice and entry format
- Do not self-star or brigade - let the listing earn traction organically
**Test scenarios:**
- Test expectation: none - external PRs, not code
**Verification:**
- All 5 PRs opened on the same day (batch effort, reduces overhead)
- Entries include: skill name, one-sentence description matching tuned SKILL.md copy, release URL, source repo URL
- Track merge status over 14 days; abandon PRs that go stale after reasonable nudging
- [ ] **Unit 6: Submit to Anthropic's official Plugin/Skills Marketplace**
**Goal:** Get featured in Claude Code's "Discover" tab, the closest thing to a native claude.ai skill directory.
**Requirements:** R2
**Dependencies:** Unit 1, Unit 2
**Files:**
- No repo changes; this is an external submission at `platform.claude.com/plugins/submit`
**Approach:**
- Submit via Anthropic's form with: skill name, description (matches tuned SKILL.md), GitHub repo URL, release asset URL, demo video link (from Unit 7 if available)
- Expect quality/security review; Anthropic will likely ask for the ClawGuard-scanner-style audit items already surfaced in issue #231 - have responses ready
- Do not wait for acceptance before shipping other levers
**Test scenarios:**
- Test expectation: none - external submission
**Verification:**
- Submission confirmation received
- Track review status weekly; iterate on feedback if any
- [ ] **Unit 7: Record a 15-60 second demo GIF or screen recording**
**Goal:** Produce the visual asset that every amplification channel needs - launch tweet, Neuron pitch, README hero, release notes.
**Requirements:** R6
**Dependencies:** Unit 2 (want the tuned description on-screen), Unit 3 (want the updated install flow)
**Files:**
- Create: `assets/claudeai-demo.gif` (or `.mp4` if GIF is too large)
- Modify: `README.md` to embed the GIF
**Approach:**
- Two possible framings:
1. "Upload + use" flow: 15 seconds showing Upload dialog -> skill appears -> sample query -> result
2. "One query" flow: 15-30 seconds of a real research query running end-to-end with actual output
- Pick framing 2 for outside-audience amplification (tweet, Neuron); framing 1 for the README
- Record at 1x speed (speeding up feels fake); edit to =60 seconds
- Export as optimized GIF or H.264 MP4 =5MB
**Test scenarios:**
- Test expectation: none - media asset
**Verification:**
- Asset loads cleanly in GitHub README
- Asset uploads cleanly to X (under their video length/size caps)
- Matt watches it fresh and the "oh wow" moment is unambiguous in the first 5 seconds
- [ ] **Unit 8: Pitch The Neuron "AI Skill of the Day"**
**Goal:** One high-leverage newsletter placement that historically drives the biggest external install spike for Claude skills.
**Requirements:** R6
**Dependencies:** Unit 1, Unit 7
**Approach:**
- Identify The Neuron editor contact (newsletter footer, X DMs, their `skilloftheday@` alias if published)
- Pitch with: 3-sentence hook, demo video link, release URL, 3 example queries that show breadth
- Angle: "researcher skill that queries 12+ social sources in one shot" - novelty vs. their typical dev-tool coverage
- Offer exclusive timing if they want (publish first, then we tweet)
- Do not follow up more than twice
**Test scenarios:**
- Test expectation: none - external pitch
**Verification:**
- Pitch sent with all assets linked
- Track whether the issue ships within 14 days; if not, reuse the materials for other newsletters
- [ ] **Unit 9: Launch tweet + recurring news-cycle posts**
**Goal:** Seed social discovery and establish a weekly cadence so the skill stays top-of-mind.
**Requirements:** R6
**Dependencies:** Unit 1, Unit 7
**Approach:**
- Launch tweet: demo GIF + 1-sentence description + install link. Post to X, cross-post to r/ClaudeAI and r/singularity same day.
- Do not tag handles reflexively - research each target account's culture first
- Weekly recurring pattern: pick a news moment (earnings, launch, election, cultural event), run `last30days` on it, screenshot the output, post. Low-effort, repeatable, compounds.
- Track: likes, impressions, link-click referrer traffic to the release page
**Test scenarios:**
- Test expectation: none - social posts
**Verification:**
- Launch tweet live with demo GIF
- At least one follow-up news-cycle post within 7 days
- Referrer traffic spike visible in GitHub traffic dashboard
- [ ] **Unit 10: Adoption telemetry and feedback loop**
**Goal:** Learn which levers work so we double down on wins and cut losses. Current blind spot: no visibility into claude.ai install counts.
**Requirements:** R7
**Dependencies:** Unit 1
**Approach:**
- Baseline metrics (capture on Unit 1 ship day):
- GitHub stars
- Clones/day
- Traffic referrers
- Release-asset download count (GitHub exposes this on the Release page)
- Weekly review during the first 6 weeks of:
- Release download deltas
- Star velocity
- Referrer sources (identifies which aggregator/newsletter/tweet drove traffic)
- New GitHub issues that mention claude.ai specifically
- No dedicated analytics infrastructure - use what GitHub provides + manual referrer spot-checks
- Publish a "what worked / what didn't" retro after 6 weeks in `docs/solutions/` so the next launch compounds
**Test scenarios:**
- Test expectation: none - observability
**Verification:**
- Baseline metrics captured in a `docs/solutions/YYYY-MM-DD-*.md` note
- Weekly log of download/star/referrer deltas maintained
- Retro written at week 6 with concrete learnings for the next release
## System-Wide Impact
- **Interaction graph:** Touches GitHub (release, CI), external aggregators (PRs), Anthropic marketplace (submission), X/Reddit/newsletter (social), ClawHub/Gemini listings (cross-links). No runtime code changes.
- **State lifecycle risks:** Minimal. The main risk is inconsistent cross-linking (some surfaces mention claude.ai, others don't) - Unit 4 treats this as a coordinated sweep, not per-surface creep.
- **API surface parity:** None - no API changes.
- **Integration coverage:** The critical integration is trigger-selection behavior (Unit 2). Manual verification across web / Claude Code / OpenClaw is the gate.
- **Unchanged invariants:** Runtime pipeline, existing install paths (Claude Code plugin / OpenClaw / Gemini) all stay working. Zero-config first-run for default sources remains intact.
## Risks and Dependencies
| Risk | Mitigation |
|------|------------|
| Anthropic marketplace rejects the submission on security/quality grounds | Run `scripts/build-skill.sh` output through ClawGuard or equivalent scanner pre-submission; address #231 findings if real |
| Awesome-list maintainers reject or ignore PRs | Submit to 5 lists in parallel; any 2 acceptances are enough; do not brigade |
| The Neuron pitch is ignored | Treat as upside, not critical path; reuse materials for other newsletters (Ben's Bites, TLDR, Superhuman AI) |
| Tuned description causes false-positive skill selection on unrelated prompts | Manual prompt-testing in Unit 2; be willing to walk back if Claude over-invokes the skill |
| A new Anthropic marketplace or directory launches mid-plan and changes the landscape | The research-tracking cadence in Unit 10 catches this within a week; plan can adapt |
| Launch tweet flops / no organic pickup | Weekly news-cycle cadence (Unit 9) is the compounding play, not the launch moment |
| Cross-repo cross-links in Unit 4 go stale when listings move | Use canonical GitHub Release URL (deterministic) as the link target everywhere |
## Documentation / Operational Notes
- README gets a hero section update in Unit 3
- CHANGELOG gets a v3.0.1 release-notes entry (already shipped in plan 001)
- A `docs/solutions/` retrospective note ships after the 6-week observation window (Unit 10)
- No runbook needed - distribution work is one-time-per-release
## Sources and References
- Research pass by repo-research-analyst on 2026-04-14 - [findings](https://github.com/anthropics/skills)
- [Anthropic Plugin/Skills Marketplace submissions](https://platform.claude.com/plugins/submit)
- [anthropics/skills](https://github.com/anthropics/skills) - 87k stars, canonical repo
- [SkillsMP](https://skillsmp.com), [claudeskills.info](https://claudeskills.info), [mcpmarket.com/tools/skills](https://mcpmarket.com/tools/skills) - aggregators
- [ComposioHQ/awesome-claude-skills](https://github.com/ComposioHQ/awesome-claude-skills)
- [sickn33/antigravity-awesome-skills](https://github.com/sickn33/antigravity-awesome-skills)
- [The Neuron Skill of the Day digest](https://www.theneuron.ai/explainer-articles/the-neurons-ai-skill-of-the-day-digest-april-2026-week-1/)
- Completed prerequisite: `docs/plans/2026-04-14-001-fix-skill-upload-200-file-limit-plan.md` (packaging fix)
- Related code: `SKILL.md` (frontmatter), `README.md`, `scripts/build-skill.sh`, `.github/workflows/`
- Install-count reference points from aggregators: self-improving-agent 357k, frontend-design 277k, skill-vetter 190k, github 148k, proactive-agent 135k