forkfeed-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/guide-content.d.ts +6 -0
- package/dist/guide-content.js +233 -0
- package/dist/image-catalog.d.ts +1152 -0
- package/dist/image-catalog.js +232 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +181 -0
- package/package.json +28 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Condensed skill guide for generating forkfeed content from GitHub commits.
|
|
3
|
+
* Returned by the forkfeed_guide tool. This is the single-read reference
|
|
4
|
+
* that teaches Claude how to generate a valid manifest.
|
|
5
|
+
*/
|
|
6
|
+
export const GUIDE_CONTENT = `
|
|
7
|
+
# Forkfeed Content Guide - "The Fuck I Pushed"
|
|
8
|
+
|
|
9
|
+
Turn GitHub commits into swipeable card content. One fork per repo, one feed per commit, 8 cards per feed (35-62 variants total).
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
1. Ask which repo and commits to process
|
|
14
|
+
2. Fetch commit data via git or gh CLI
|
|
15
|
+
3. Generate 8-card manifest JSON
|
|
16
|
+
4. Call forkfeed_push with the manifest
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Phase 1: Resolve repo and commits
|
|
21
|
+
|
|
22
|
+
Use the **current working directory** as the repo. Do not ask which repo.
|
|
23
|
+
|
|
24
|
+
If the user didn't specify commits, use the latest commit. Otherwise respect what they asked (last N, specific SHA, since date).
|
|
25
|
+
|
|
26
|
+
Use **IT Scenes** images. Call **forkfeed_images** to get the full catalog (200 scene images + 30 backgrounds). Match images to content by tags and semantic similarity. Do not ask about image style.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Phase 2: Fetch commit data
|
|
31
|
+
|
|
32
|
+
### GitHub repos
|
|
33
|
+
\`\`\`bash
|
|
34
|
+
# Verify access
|
|
35
|
+
gh api repos/{owner}/{repo} --jq '.full_name'
|
|
36
|
+
|
|
37
|
+
# Metadata
|
|
38
|
+
gh api repos/{owner}/{repo}/commits/{sha} --jq '{sha: .sha, shortSha: (.sha[:7]), message: .commit.message, author: .commit.author.name, date: .commit.author.date, additions: .stats.additions, deletions: .stats.deletions, totalFiles: (.files | length)}'
|
|
39
|
+
|
|
40
|
+
# Full diff
|
|
41
|
+
gh api repos/{owner}/{repo}/commits/{sha} -H "Accept: application/vnd.github.v3.diff"
|
|
42
|
+
|
|
43
|
+
# README for fork title/description
|
|
44
|
+
gh api repos/{owner}/{repo} --jq '.description'
|
|
45
|
+
\`\`\`
|
|
46
|
+
|
|
47
|
+
### Local repos
|
|
48
|
+
\`\`\`bash
|
|
49
|
+
git -C "{path}" log -1 --format='{"sha":"%H","shortSha":"%h","message":"%s","author":"%an","date":"%aI"}' {sha}
|
|
50
|
+
git -C "{path}" diff --stat {sha}^..{sha}
|
|
51
|
+
git -C "{path}" diff {sha}^..{sha}
|
|
52
|
+
\`\`\`
|
|
53
|
+
|
|
54
|
+
Skip merge commits (>1 parent) unless it's the only commit.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Phase 3: Generate manifest
|
|
59
|
+
|
|
60
|
+
### ID conventions
|
|
61
|
+
- Fork: \`tfip-{owner}-{repo}\` or \`tfip-local-{dirname}\`
|
|
62
|
+
- Feed: \`tfip-{owner}-{repo}-{7char-sha}\`
|
|
63
|
+
- Card: UUID v4 (generate upfront with \`node -e "for(let i=0;i<N;i++) process.stdout.write(require('crypto').randomUUID()+'\\n')"\`)
|
|
64
|
+
|
|
65
|
+
All IDs must match \`/^[a-z0-9-]+$/\` (except card UUIDs).
|
|
66
|
+
|
|
67
|
+
### JSON structure
|
|
68
|
+
\`\`\`json
|
|
69
|
+
{
|
|
70
|
+
"forks": [{
|
|
71
|
+
"_id": "tfip-owner-repo",
|
|
72
|
+
"title": "Project Name (from README)",
|
|
73
|
+
"description": "What the project IS",
|
|
74
|
+
"imageSrc": "same as card 0 cover image",
|
|
75
|
+
"feedIds": ["tfip-owner-repo-abc1234"],
|
|
76
|
+
"actionLabel": "View on GitHub",
|
|
77
|
+
"actionUrl": "https://github.com/owner/repo"
|
|
78
|
+
}],
|
|
79
|
+
"feeds": [{
|
|
80
|
+
"_id": "tfip-owner-repo-abc1234",
|
|
81
|
+
"title": "Human-readable headline (max 60 chars)",
|
|
82
|
+
"description": "Mon DD: one-line impact summary",
|
|
83
|
+
"imageSrc": "same as card 0 cover image",
|
|
84
|
+
"mode": "sequential",
|
|
85
|
+
"scrollDirection": "vertical",
|
|
86
|
+
"engagement": true
|
|
87
|
+
}],
|
|
88
|
+
"cards": [
|
|
89
|
+
{
|
|
90
|
+
"_id": "uuid-v4",
|
|
91
|
+
"feedId": "tfip-owner-repo-abc1234",
|
|
92
|
+
"order": 0,
|
|
93
|
+
"variants": [
|
|
94
|
+
{ "type": "FULL_IMAGE", "imageSrc": "url", "title": "Section name", "subtitle": "Hook text (max 200 chars)" },
|
|
95
|
+
{ "type": "CONTENT", "backgroundSrc": "url", "blocks": [...] }
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}
|
|
100
|
+
\`\`\`
|
|
101
|
+
|
|
102
|
+
### The 8 cards (sections)
|
|
103
|
+
|
|
104
|
+
Each card: variant[0] = FULL_IMAGE cover, variant[1+] = CONTENT details.
|
|
105
|
+
|
|
106
|
+
| # | Section | Detail variants | Block pattern | Tone |
|
|
107
|
+
|---|---------|----------------|---------------|------|
|
|
108
|
+
| 0 | Explain like I'm 5 | 3-6 | IMAGE(wide) -> TITLE -> TEXT | Playful, zero jargon |
|
|
109
|
+
| 1 | The roast | 3-6 | IMAGE(wide) -> TITLE -> TEXT, optional SUBTEXT | Maximum cheekiness, swearing ok |
|
|
110
|
+
| 2 | Commit message, decoded | 2-4 | SOCIAL -> TITLE -> TEXT -> optional CODE | Escalating absurdity |
|
|
111
|
+
| 3 | The LinkedIn post | 2-4 | SOCIAL -> TITLE -> TEXT -> SUBTEXT | Peak LinkedIn parody |
|
|
112
|
+
| 4 | Statistics | 3-5 | IMAGE(wide) -> TITLE -> CODE | Deadpan data humor |
|
|
113
|
+
| 5 | Learning moment | 3-8 | IMAGE(wide) -> TITLE -> TEXT -> optional CODE -> BUTTON(last) | Teach with personality |
|
|
114
|
+
| 6 | Alternatives | 3-6 | IMAGE(wide) -> TITLE -> TEXT -> optional CODE | Constructive snark |
|
|
115
|
+
| 7 | Quiz | 10-15 | TITLE -> QUIZ (no IMAGE) | Quiz show energy |
|
|
116
|
+
|
|
117
|
+
### Content block types
|
|
118
|
+
|
|
119
|
+
\`\`\`typescript
|
|
120
|
+
// CONTENT_IMAGE - inline image
|
|
121
|
+
{ type: "CONTENT_IMAGE", imageSrc: "url", sizing: "wide" }
|
|
122
|
+
|
|
123
|
+
// CONTENT_TITLE - section heading (required in every detail variant, sentence case)
|
|
124
|
+
{ type: "CONTENT_TITLE", title: "Feature detection pattern" }
|
|
125
|
+
|
|
126
|
+
// CONTENT_TEXT - paragraph (50-200 words, use \\n\\n for breaks)
|
|
127
|
+
{ type: "CONTENT_TEXT", text: "..." }
|
|
128
|
+
|
|
129
|
+
// CONTENT_CODE - code snippet (real from diff, 5-20 lines, strip +/- prefixes)
|
|
130
|
+
{ type: "CONTENT_CODE", code: "...", language: "typescript" }
|
|
131
|
+
|
|
132
|
+
// CONTENT_SOCIAL - persona post (Cards 2-3 only, replaces IMAGE)
|
|
133
|
+
{ type: "CONTENT_SOCIAL", avatarSrc: "url", name: "Chad Gitpush", subtitle: "10x Engineer", source: "linkedin" }
|
|
134
|
+
|
|
135
|
+
// CONTENT_SUBTEXT - aside or protip
|
|
136
|
+
{ type: "CONTENT_SUBTEXT", text: "..." }
|
|
137
|
+
|
|
138
|
+
// CONTENT_QUIZ - quiz question (Card 7 only, exactly 4 options, one correct)
|
|
139
|
+
{ type: "CONTENT_QUIZ", question: "...", options: [{ label: "A", correct: false }, ...], explanation: "..." }
|
|
140
|
+
|
|
141
|
+
// CONTENT_BUTTON - action button (Card 5 only, MUST be last block)
|
|
142
|
+
{ type: "CONTENT_BUTTON", label: "Google it", action: "url", target: "https://www.google.com/search?q=topic+here" }
|
|
143
|
+
\`\`\`
|
|
144
|
+
|
|
145
|
+
### Card 2 personas (Decoded)
|
|
146
|
+
- "What you meant" (source: "x" or "threads")
|
|
147
|
+
- "Corporate speak" (source: "linkedin")
|
|
148
|
+
- Optional: "Shakespearean" (source: "x"), "Military briefing" (source: "x")
|
|
149
|
+
|
|
150
|
+
### Card 3 personas (LinkedIn)
|
|
151
|
+
- Chad Gitpush: "10x Engineer | Building in Public | DMs Open"
|
|
152
|
+
- Alexandra Middleware: "VP of Forward Thinking at TechCorp"
|
|
153
|
+
- Dave 'Shipping' Johnson: "Just a humble engineer doing humble things"
|
|
154
|
+
|
|
155
|
+
### Key rules
|
|
156
|
+
- All CONTENT variants in a card share the same backgroundSrc
|
|
157
|
+
- 8 unique backgroundSrc values across the 8 cards
|
|
158
|
+
- 8 unique FULL_IMAGE cover imageSrc values
|
|
159
|
+
- Cards 0-5: code must be REAL from the diff (never fabricated)
|
|
160
|
+
- Card 6: synthesized code IS allowed
|
|
161
|
+
- Card 7: no code blocks, no CONTENT_IMAGE
|
|
162
|
+
- Card 5: every detail variant MUST end with CONTENT_BUTTON
|
|
163
|
+
- CONTENT_QUIZ: exactly 4 options, exactly one correct: true, explanation required
|
|
164
|
+
- No em dashes, smart quotes, or non-ASCII characters
|
|
165
|
+
- Top-level key MUST be "forks" (plural array)
|
|
166
|
+
|
|
167
|
+
### IT Scenes images
|
|
168
|
+
|
|
169
|
+
Call **forkfeed_images** to get the full catalog. 200 scene images (img1-img200) for covers and inline images, 30 backgrounds (bg1-bg30) for card backgrounds.
|
|
170
|
+
|
|
171
|
+
**Matching rules:**
|
|
172
|
+
1. Identify commit tags from the diff: deploy, git, disaster, debug, hype, victory, beginner, language, lifestyle, workplace, sarcastic, general
|
|
173
|
+
2. Filter images by tag overlap
|
|
174
|
+
3. Rank by semantic similarity to card content (title, text, code topics)
|
|
175
|
+
4. Assign best match, remove from pool (no duplicates)
|
|
176
|
+
|
|
177
|
+
**Uniqueness rules:**
|
|
178
|
+
- 8 unique scene images for FULL_IMAGE covers (one per card)
|
|
179
|
+
- Detail CONTENT_IMAGE must differ from the card's cover
|
|
180
|
+
- No two detail variants in the same card share a CONTENT_IMAGE
|
|
181
|
+
- 8 unique bg* images for backgroundSrc (one per card)
|
|
182
|
+
- Pick 16-25 unique images per feed
|
|
183
|
+
|
|
184
|
+
**BG preferences per section:**
|
|
185
|
+
- ELI5 (0): bg10, bg27 | Roast (1): bg11, bg1 | Decoded (2): bg20, bg11
|
|
186
|
+
- LinkedIn (3): bg25, bg24 | Statistics (4): bg9, bg3 | Learning (5): match tech
|
|
187
|
+
- Alternatives (6): bg25, bg30 | Quiz (7): bg18, bg5
|
|
188
|
+
|
|
189
|
+
**Fork/feed imageSrc** = Card 0's FULL_IMAGE imageSrc
|
|
190
|
+
|
|
191
|
+
### Tone
|
|
192
|
+
Casual, cheeky, technically accurate. Like your funniest friend reviewing your code.
|
|
193
|
+
- Use "you" directly
|
|
194
|
+
- Short paragraphs
|
|
195
|
+
- Contractions
|
|
196
|
+
- Swear occasionally for emphasis
|
|
197
|
+
- Humor is the default
|
|
198
|
+
|
|
199
|
+
### Incremental updates
|
|
200
|
+
If the user has pushed before (check with forkfeed_status), generate only NEW feeds/cards. Prepend new feed IDs to the fork's feedIds array. Never regenerate existing commits.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Phase 4: Push
|
|
205
|
+
|
|
206
|
+
After generating the manifest JSON, call the **forkfeed_push** tool with it:
|
|
207
|
+
\`\`\`
|
|
208
|
+
forkfeed_push({ manifest: { forks: [...], feeds: [...], cards: [...] } })
|
|
209
|
+
\`\`\`
|
|
210
|
+
|
|
211
|
+
Content starts as **private**. The user can make it public from the forkfeed mobile app (requires admin approval).
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Validation checklist (verify before pushing)
|
|
216
|
+
|
|
217
|
+
- [ ] Fork/feed IDs match /^[a-z0-9-]+$/
|
|
218
|
+
- [ ] Card IDs are valid UUID v4
|
|
219
|
+
- [ ] Every card.feedId matches an actual feed._id
|
|
220
|
+
- [ ] Fork.feedIds match actual feed._id values
|
|
221
|
+
- [ ] Top-level key is "forks" (plural array)
|
|
222
|
+
- [ ] Card order values are sequential 0-7
|
|
223
|
+
- [ ] Each card has >= 2 variants
|
|
224
|
+
- [ ] variant[0] is FULL_IMAGE with imageSrc, title, subtitle
|
|
225
|
+
- [ ] variant[1+] are CONTENT with backgroundSrc and blocks
|
|
226
|
+
- [ ] FULL_IMAGE subtitle max 200 chars
|
|
227
|
+
- [ ] Feed title max 60 chars
|
|
228
|
+
- [ ] Feed mode: "sequential", scrollDirection: "vertical", engagement: true
|
|
229
|
+
- [ ] CONTENT_QUIZ: exactly 4 options, one correct, explanation required
|
|
230
|
+
- [ ] Card 5 detail variants end with CONTENT_BUTTON
|
|
231
|
+
- [ ] 8 unique cover images, 8 unique backgrounds
|
|
232
|
+
- [ ] No em dashes or smart quotes
|
|
233
|
+
`.trim();
|