forkfeed-mcp 1.0.15 → 1.2.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.
@@ -6,143 +6,88 @@
6
6
  export const GUIDE_CONTENT = `
7
7
  # Forkfeed Content Guide - "The Fuck I Pushed"
8
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).
9
+ Turn GitHub commits into swipeable card content. One fork per repo, one feed per commit, 8 cards per feed.
10
10
 
11
- **Important: process exactly ONE commit at a time, never multiple commits in a single run.**
11
+ **Process exactly ONE commit at a time, never multiple.**
12
12
 
13
13
  ## Quick start
14
14
 
15
- 1. Ask which single commit to process
16
- 2. Fetch commit data via git or gh CLI
17
- 3. Generate 8-card manifest JSON
18
- 4. Call forkfeed_push with the manifest
15
+ 1. Call **forkfeed_guide** and **forkfeed_commits()** in parallel
16
+ 2. Show commits table, ask which ONE to process
17
+ 3. Call **forkfeed_commits(sha)** to get diff, stats, and pre-filtered images
18
+ 4. Generate simplified content JSON (see format below)
19
+ 5. Call **forkfeed_build** with the content (push defaults to true)
19
20
 
20
21
  ---
21
22
 
22
- ## Phase 1: Resolve repo and commit
23
+ ## Simplified content format (for forkfeed_build)
23
24
 
24
- Use the **current working directory** as the repo. Do not ask which repo.
25
+ The builder handles UUIDs, full image URLs, cover variants, type wrappers, feedId/order, and fork/feed boilerplate. You only provide creative content.
25
26
 
26
- Process exactly one commit per run. If the user didn't specify, use the latest commit. If they ask for multiple commits, process only the first and tell them to run again for the next.
27
-
28
- 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.
29
-
30
- ---
31
-
32
- ## Phase 2: Fetch commit data
33
-
34
- ### GitHub repos
35
- \`\`\`bash
36
- # Verify access
37
- gh api repos/{owner}/{repo} --jq '.full_name'
38
-
39
- # Metadata
40
- 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)}'
41
-
42
- # Full diff
43
- gh api repos/{owner}/{repo}/commits/{sha} -H "Accept: application/vnd.github.v3.diff"
44
-
45
- # README for fork title/description
46
- gh api repos/{owner}/{repo} --jq '.description'
47
- \`\`\`
48
-
49
- ### Local repos
50
- \`\`\`bash
51
- git -C "{path}" log -1 --format='{"sha":"%H","shortSha":"%h","message":"%s","author":"%an","date":"%aI"}' {sha}
52
- git -C "{path}" diff --stat {sha}^..{sha}
53
- git -C "{path}" diff {sha}^..{sha}
54
- \`\`\`
55
-
56
- Skip merge commits (>1 parent) unless it's the only commit.
57
-
58
- ---
59
-
60
- ## Phase 3: Generate manifest
61
-
62
- ### ID conventions
63
- - Fork: \`tfip-{owner}-{repo}\` or \`tfip-local-{dirname}\`
64
- - Feed: \`tfip-{owner}-{repo}-{7char-sha}\`
65
- - Card: UUID v4 (generate upfront with \`node -e "for(let i=0;i<N;i++) process.stdout.write(require('crypto').randomUUID()+'\\n')"\`)
66
-
67
- All IDs must match \`/^[a-z0-9-]+$/\` (except card UUIDs).
68
-
69
- ### JSON structure
70
27
  \`\`\`json
71
28
  {
72
- "forks": [{
73
- "_id": "tfip-owner-repo",
74
- "title": "Project Name (from README)",
75
- "description": "What the project IS",
76
- "imageSrc": "same as card 0 cover image",
77
- "feedIds": ["tfip-owner-repo-abc1234"],
78
- "actionLabel": "View on GitHub",
79
- "actionUrl": "https://github.com/owner/repo"
80
- }],
81
- "feeds": [{
82
- "_id": "tfip-owner-repo-abc1234",
83
- "title": "Human-readable headline (max 60 chars)",
84
- "description": "Mon DD: one-line impact summary",
85
- "imageSrc": "same as card 0 cover image",
86
- "mode": "sequential",
87
- "scrollDirection": "vertical",
88
- "engagement": true
89
- }],
29
+ "owner": "owner-or-local",
30
+ "repo": "repo-name",
31
+ "sha": "7char",
32
+ "feedTitle": "Max 60 chars headline",
33
+ "feedDescription": "Mon DD: one-line impact summary",
34
+ "forkTitle": "Project Name",
35
+ "forkDescription": "What the project IS",
36
+ "actionUrl": "https://github.com/owner/repo",
37
+ "existingFeedIds": [],
38
+ "images": {
39
+ "bg": ["bg10", "bg11", "bg20", "bg25", "bg9", "bg12", "bg30", "bg18"]
40
+ },
90
41
  "cards": [
91
42
  {
92
- "_id": "uuid-v4",
93
- "feedId": "tfip-owner-repo-abc1234",
94
- "order": 0,
95
43
  "variants": [
96
- { "type": "FULL_IMAGE", "imageSrc": "url", "title": "Section name", "subtitle": "Hook text (max 200 chars)" },
97
- { "type": "CONTENT", "backgroundSrc": "url", "blocks": [...] }
44
+ { "blocks": [{ "img": "img47", "sizing": "wide" }, { "title": "Section heading" }, { "text": "Paragraph content..." }] },
45
+ { "blocks": [{ "title": "Another section" }, { "text": "More content..." }] }
98
46
  ]
99
47
  }
100
48
  ]
101
49
  }
102
50
  \`\`\`
103
51
 
104
- ### The 8 cards (sections)
105
-
106
- Each card: variant[0] = FULL_IMAGE cover, variant[1+] = CONTENT details.
107
-
108
- | # | Section | Detail variants | Block pattern | Tone |
109
- |---|---------|----------------|---------------|------|
110
- | 0 | Explain like I'm 5 | 3-6 | IMAGE(wide) -> TITLE -> TEXT | Playful, zero jargon |
111
- | 1 | The roast | 3-6 | IMAGE(wide) -> TITLE -> TEXT, optional SUBTEXT | Maximum cheekiness, swearing ok |
112
- | 2 | Commit message, decoded | 2-4 | SOCIAL -> TITLE -> TEXT -> optional CODE | Escalating absurdity |
113
- | 3 | The LinkedIn post | 2-4 | SOCIAL -> TITLE -> TEXT -> SUBTEXT | Peak LinkedIn parody |
114
- | 4 | Statistics | 3-5 | IMAGE(wide) -> TITLE -> CODE | Deadpan data humor |
115
- | 5 | Learning moment | 3-8 | IMAGE(wide) -> TITLE -> TEXT -> optional CODE -> BUTTON(last) | Teach with personality |
116
- | 6 | Alternatives | 3-6 | IMAGE(wide) -> TITLE -> TEXT -> optional CODE | Constructive snark |
117
- | 7 | Quiz | 10-15 | TITLE -> QUIZ (no IMAGE) | Quiz show energy |
118
-
119
- ### Content block types
120
-
121
- \`\`\`typescript
122
- // CONTENT_IMAGE - inline image
123
- { type: "CONTENT_IMAGE", imageSrc: "url", sizing: "wide" }
124
-
125
- // CONTENT_TITLE - section heading (required in every detail variant, sentence case)
126
- { type: "CONTENT_TITLE", title: "Feature detection pattern" }
52
+ ### Block types (inferred from fields)
53
+
54
+ | Write this | Becomes | Notes |
55
+ |------------|---------|-------|
56
+ | \`{ "img": "img47" }\` | CONTENT_IMAGE | sizing defaults to "wide". Options: automatic, wide, portrait, square, small_portrait |
57
+ | \`{ "img": "img47", "sizing": "square" }\` | CONTENT_IMAGE | With explicit sizing |
58
+ | \`{ "title": "Heading" }\` | CONTENT_TITLE | Required in every detail variant, sentence case |
59
+ | \`{ "text": "Paragraph..." }\` | CONTENT_TEXT | 50-200 words, use \\\\n\\\\n for breaks |
60
+ | \`{ "code": "const x = 1", "lang": "typescript" }\` | CONTENT_CODE | Real code from diff, 5-20 lines, strip +/- prefixes |
61
+ | \`{ "subtext": "Pro tip..." }\` | CONTENT_SUBTEXT | Aside or protip |
62
+ | \`{ "name": "Chad", "subtitle": "10x Engineer", "avatar": "img98", "source": "linkedin" }\` | CONTENT_SOCIAL | Cards 2-3 only. source: x, linkedin, threads, etc. |
63
+ | \`{ "question": "What does X do?", "options": [{"label": "A", "correct": false}, {"label": "B", "correct": true}], "explanation": "Because..." }\` | CONTENT_QUIZ | Card 7 only. 4 options recommended, min 2, one correct, always include explanation |
64
+ | \`{ "label": "Google it", "action": "url", "target": "https://google.com/search?q=topic" }\` | CONTENT_BUTTON | Card 5 only, MUST be last block in every detail variant |
65
+
66
+ ### What the builder does automatically
67
+ - Generates UUID v4 for each card ID
68
+ - Resolves short image IDs (img47, bg10) to full CDN URLs
69
+ - Adds FULL_IMAGE cover variant with fixed title/subtitle per card type
70
+ - Sets backgroundSrc on all CONTENT variants from images.bg[cardIndex]
71
+ - Sets feedId and order on each card
72
+ - Builds fork and feed objects with correct IDs and metadata
73
+ - Validates the assembled manifest before pushing
127
74
 
128
- // CONTENT_TEXT - paragraph (50-200 words, use \\n\\n for breaks)
129
- { type: "CONTENT_TEXT", text: "..." }
130
-
131
- // CONTENT_CODE - code snippet (real from diff, 5-20 lines, strip +/- prefixes)
132
- { type: "CONTENT_CODE", code: "...", language: "typescript" }
133
-
134
- // CONTENT_SOCIAL - persona post (Cards 2-3 only, replaces IMAGE)
135
- { type: "CONTENT_SOCIAL", avatarSrc: "url", name: "Chad Gitpush", subtitle: "10x Engineer", source: "linkedin" }
75
+ ---
136
76
 
137
- // CONTENT_SUBTEXT - aside or protip
138
- { type: "CONTENT_SUBTEXT", text: "..." }
77
+ ## The 8 cards
139
78
 
140
- // CONTENT_QUIZ - quiz question (Card 7 only, exactly 4 options, one correct)
141
- { type: "CONTENT_QUIZ", question: "...", options: [{ label: "A", correct: false }, ...], explanation: "..." }
79
+ Each card = array of detail variants (cover is auto-generated). Provide 1+ variants per card.
142
80
 
143
- // CONTENT_BUTTON - action button (Card 5 only, MUST be last block)
144
- { type: "CONTENT_BUTTON", label: "Google it", action: "url", target: "https://www.google.com/search?q=topic+here" }
145
- \`\`\`
81
+ | # | Section | Detail variants | Block pattern | Tone |
82
+ |---|---------|----------------|---------------|------|
83
+ | 0 | Explain like I'm 5 | 3-6 | img -> title -> text | Playful, zero jargon |
84
+ | 1 | The roast | 3-6 | img -> title -> text, optional subtext | Maximum cheekiness, swearing ok |
85
+ | 2 | Commit message, decoded | 2-4 | social -> title -> text -> optional code | Escalating absurdity |
86
+ | 3 | The LinkedIn post | 2-4 | social -> title -> text -> subtext | Peak LinkedIn parody |
87
+ | 4 | Statistics | 3-5 | img -> title -> code | Deadpan data humor |
88
+ | 5 | Learning moment | 3-8 | img -> title -> text -> optional code -> button(last) | Teach with personality |
89
+ | 6 | Alternatives | 3-6 | img -> title -> text -> optional code | Constructive snark |
90
+ | 7 | Quiz | 10-15 | title -> quiz (no img blocks) | Quiz show energy |
146
91
 
147
92
  ### Card 2 personas (Decoded)
148
93
  - "What you meant" (source: "x" or "threads")
@@ -154,127 +99,48 @@ Each card: variant[0] = FULL_IMAGE cover, variant[1+] = CONTENT details.
154
99
  - Alexandra Middleware: "VP of Forward Thinking at TechCorp"
155
100
  - Dave 'Shipping' Johnson: "Just a humble engineer doing humble things"
156
101
 
157
- ### Key rules
158
- - All CONTENT variants in a card share the same backgroundSrc
159
- - 8 unique backgroundSrc values across the 8 cards
160
- - 8 unique FULL_IMAGE cover imageSrc values
102
+ ---
103
+
104
+ ## Key rules
105
+
106
+ - Exactly 8 cards in the cards array (one per section above)
107
+ - images.bg must have 8 unique background IDs
108
+ - 12-20 unique scene images (img*) across the feed, no duplicate within same card
161
109
  - Cards 0-5: code must be REAL from the diff (never fabricated)
162
110
  - Card 6: synthesized code IS allowed
163
- - Card 7: no code blocks, no CONTENT_IMAGE
164
- - Card 5: every detail variant MUST end with CONTENT_BUTTON
165
- - CONTENT_QUIZ: exactly 4 options, exactly one correct: true, explanation required
111
+ - Card 7: no img blocks, quiz blocks only (with title per variant)
112
+ - Card 5: every detail variant MUST end with a button block
113
+ - Cards 2-3: first block should be social (not img)
114
+ - CONTENT_QUIZ: 4 options recommended (min 2), exactly one correct, always include explanation
166
115
  - No em dashes, smart quotes, or non-ASCII characters
167
- - Top-level key MUST be "forks" (plural array)
168
-
169
- ### IT Scenes images
170
-
171
- 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.
116
+ - Feed title max 60 chars
172
117
 
173
- **Matching rules:**
174
- 1. Identify commit tags from the diff: deploy, git, disaster, debug, hype, victory, beginner, language, lifestyle, workplace, sarcastic, general
175
- 2. Filter images by tag overlap
176
- 3. Rank by semantic similarity to card content (title, text, code topics)
177
- 4. Assign best match, remove from pool (no duplicates)
118
+ ### Images
178
119
 
179
- **Uniqueness rules:**
180
- - 8 unique scene images for FULL_IMAGE covers (one per card)
181
- - Detail CONTENT_IMAGE must differ from the card's cover
182
- - No two detail variants in the same card share a CONTENT_IMAGE
183
- - 8 unique bg* images for backgroundSrc (one per card)
184
- - Pick 16-25 unique images per feed
120
+ Images are pre-filtered by **forkfeed_commits(sha)**. Pick from the suggested list.
185
121
 
186
- **BG preferences per section:**
187
- - ELI5 (0): bg10, bg27 | Roast (1): bg11, bg1 | Decoded (2): bg20, bg11
188
- - LinkedIn (3): bg25, bg24 | Statistics (4): bg9, bg3 | Learning (5): match tech
189
- - Alternatives (6): bg25, bg30 | Quiz (7): bg18, bg5
190
-
191
- **Fork/feed imageSrc** = Card 0's FULL_IMAGE imageSrc
122
+ **BG preferences:** ELI5: bg10/bg27 | Roast: bg11/bg1 | Decoded: bg20/bg11 | LinkedIn: bg25/bg24 | Stats: bg9/bg3 | Learning: match tech | Alts: bg25/bg30 | Quiz: bg18/bg5
192
123
 
193
124
  ### Tone
194
- Casual, cheeky, technically accurate. Like your funniest friend reviewing your code.
195
- - Use "you" directly
196
- - Short paragraphs
197
- - Contractions
198
- - Swear occasionally for emphasis
199
- - Humor is the default
125
+ Casual, cheeky, technically accurate. Use "you," contractions, short paragraphs, occasional swearing. Humor is the default.
200
126
 
201
127
  ### Incremental updates
202
- Always call **forkfeed_status** before generating content. It lists published feeds by their externalFeedId (which contains the commit SHA, e.g. \`tfip-owner-repo-abc1234\`). When presenting commits to the user, cross-reference with status results: match the 7-char SHA in each feed ID against commit SHAs to show which commits already have feeds.
203
-
204
- - **New commit**: Generate only the new feed and cards. The fork's feedIds must include ALL existing feed IDs plus the new one. Missing old IDs causes them to be deleted.
205
- - **Existing commit**: Warn the user that this commit already has a feed. Only regenerate if they confirm. The old cards will be replaced.
206
- - **One commit per run**: Never process multiple commits at once. If the user wants more, they run the command again.
207
-
208
- ### Parallel card generation
209
-
210
- Generate all 8 cards simultaneously using the Agent tool. This is critical for speed.
128
+ **forkfeed_commits()** (list mode) shows which commits have published feeds and lists existing feed IDs.
211
129
 
212
- **Planning phase** (main context, before launching agents):
213
- 1. Assign images upfront: pick 8 unique covers (img*), 8 unique backgrounds (bg*), and split the remaining scene images into 8 non-overlapping pools for inline CONTENT_IMAGE use per card.
214
- 2. Pre-generate 8 card UUIDs (use \`crypto.randomUUID()\` inline or a single node command).
215
- 3. Output the skeleton (fork, feed, image assignments, UUIDs) so it's visible.
216
-
217
- **Agent prompts** (launch ALL 8 in a single message):
218
- Each agent generates exactly one card. The agent prompt must include:
219
- - Card number and section name (e.g., "Card 1: The roast")
220
- - The section rules from this guide: variant count range, block pattern, tone
221
- - The full commit diff and stats
222
- - Assigned cover imageSrc (img*), backgroundSrc (bg*), and the pool of available inline images for this card
223
- - The card UUID (\`_id\`) and feed ID (\`feedId\`)
224
- - "Return ONLY the raw JSON card object, no markdown, no explanation. Do NOT use any tools, scripts, or commands - just output the JSON directly."
225
-
226
- Example agent prompt structure:
227
- \`\`\`
228
- Generate Card 2 (Commit message, decoded) for forkfeed.
229
-
230
- Card JSON structure: { "_id": "{uuid}", "feedId": "{feedId}", "order": 2, "variants": [...] }
231
-
232
- Rules: 2-4 detail variants. Pattern: SOCIAL -> TITLE -> TEXT -> optional CODE. Escalating absurdity.
233
- Personas: "What you meant" (source: "x"), "Corporate speak" (source: "linkedin").
234
-
235
- Cover: { "type": "FULL_IMAGE", "imageSrc": "{assigned-cover}", "title": "...", "subtitle": "..." }
236
- Background for all CONTENT variants: "{assigned-bg}"
237
- Available inline images: {pool}
238
-
239
- Commit diff:
240
- {diff}
241
-
242
- Return ONLY the JSON object. No markdown fences, no explanation.
243
- Do NOT use any tools, scripts, or commands. Just output the JSON directly as text.
244
- \`\`\`
245
-
246
- **Assembly** (main context, after agents complete):
247
- Collect 8 card JSONs, combine with fork and feed into the manifest, validate against the checklist, and push.
130
+ - **New commit**: Include ALL existing feed IDs in existingFeedIds. Missing old IDs causes deletion.
131
+ - **Existing commit**: Warn the user. Only regenerate if they confirm.
132
+ - **One commit per run**: Never process multiple commits at once.
248
133
 
249
134
  ---
250
135
 
251
- ## Phase 4: Push
136
+ ## Advanced: direct push with full manifest (forkfeed_push)
252
137
 
253
- After assembling the manifest, call the **forkfeed_push** tool with it:
254
- \`\`\`
255
- forkfeed_push({ manifest: { forks: [...], feeds: [...], cards: [...] } })
256
- \`\`\`
138
+ If you need full control, you can build the manifest manually and use forkfeed_push directly. The full manifest requires:
139
+ - Fork/feed objects with all fields (mode, scrollDirection, engagement, etc.)
140
+ - FULL_IMAGE cover variant[0] per card with fixed titles/subtitles
141
+ - CONTENT variants with type field, full image URLs, backgroundSrc
142
+ - UUID v4 card IDs, feedId and order on each card
257
143
 
258
- Content starts as **private**. The user can make it public from the forkfeed mobile app (requires admin approval).
259
-
260
- ---
261
-
262
- ## Validation checklist (verify before pushing)
263
-
264
- - [ ] Fork/feed IDs match /^[a-z0-9-]+$/
265
- - [ ] Card IDs are valid UUID v4
266
- - [ ] Every card.feedId matches an actual feed._id
267
- - [ ] Fork.feedIds match actual feed._id values
268
- - [ ] Top-level key is "forks" (plural array)
269
- - [ ] Card order values are sequential 0-7
270
- - [ ] Each card has >= 2 variants
271
- - [ ] variant[0] is FULL_IMAGE with imageSrc, title, subtitle
272
- - [ ] variant[1+] are CONTENT with backgroundSrc and blocks
273
- - [ ] FULL_IMAGE subtitle max 200 chars
274
- - [ ] Feed title max 60 chars
275
- - [ ] Feed mode: "sequential", scrollDirection: "vertical", engagement: true
276
- - [ ] CONTENT_QUIZ: exactly 4 options, one correct, explanation required
277
- - [ ] Card 5 detail variants end with CONTENT_BUTTON
278
- - [ ] 8 unique cover images, 8 unique backgrounds
279
- - [ ] No em dashes or smart quotes
144
+ ID conventions: Fork: \`tfip-{owner}-{repo}\`, Feed: \`tfip-{owner}-{repo}-{7char-sha}\`, Card: UUID v4
145
+ All IDs must match \`/^[a-z0-9-]+$/\` (except card UUIDs).
280
146
  `.trim();