forkfeed-mcp 1.3.4 → 1.3.6

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,7 +6,7 @@
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.
9
+ Turn GitHub commits into swipeable card content. One fork per repo, one feed per commit, 6 cards per feed.
10
10
 
11
11
  **Process exactly ONE commit at a time, never multiple.**
12
12
 
@@ -54,16 +54,16 @@ Only 5 fields + cards. Everything else is auto-populated.
54
54
  | \`{ "text": "Paragraph..." }\` | CONTENT_TEXT | 50-200 words, use \\\\n\\\\n for breaks |
55
55
  | \`{ "code": "const x = 1", "lang": "typescript" }\` | CONTENT_CODE | Real code from diff, 5-20 lines, strip +/- prefixes |
56
56
  | \`{ "subtext": "Pro tip..." }\` | CONTENT_SUBTEXT | Aside or protip |
57
- | \`{ "name": "Chad", "subtitle": "10x Engineer", "avatar": "img98", "source": "linkedin" }\` | CONTENT_SOCIAL | Cards 2-3 only. avatar can be image ID or URL. source: x, linkedin, threads, etc. |
58
- | \`{ "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 |
59
- | \`{ "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 |
57
+ | \`{ "name": "Chad", "subtitle": "10x Engineer", "avatar": "img98", "source": "linkedin" }\` | CONTENT_SOCIAL | Card 2 only. avatar can be image ID or URL. source: x, linkedin, threads, etc. |
58
+ | \`{ "question": "What does X do?", "options": [{"label": "A", "correct": false}, {"label": "B", "correct": true}], "explanation": "Because..." }\` | CONTENT_QUIZ | Card 5 only. 4 options recommended, min 2, one correct, always include explanation |
59
+ | \`{ "label": "Google it", "action": "url", "target": "https://google.com/search?q=topic" }\` | CONTENT_BUTTON | Card 3 only, MUST be last block in every detail variant |
60
60
 
61
61
  ### What the builder does automatically
62
62
  - Detects repo owner/name from git remote
63
63
  - Generates fork/feed IDs from convention (tfip-{owner}-{repo}-{sha})
64
64
  - Constructs GitHub action URL
65
65
  - Fetches existing feed IDs from server (for incremental updates)
66
- - Assigns 8 unique card backgrounds from preference table + commit tags
66
+ - Assigns 6 unique card backgrounds from preference table + commit tags
67
67
  - Generates UUID v4 for each card ID
68
68
  - Resolves short image IDs (img47, bg10) to full CDN URLs
69
69
  - Adds FULL_IMAGE cover variant with fixed title/subtitle per card type
@@ -71,13 +71,13 @@ Only 5 fields + cards. Everything else is auto-populated.
71
71
  - Validates the assembled manifest before pushing
72
72
 
73
73
  ### Optional overrides
74
- - \`bgOverride\`: array of 8 background IDs to override auto-assignment
75
- - \`coverOverride\`: array of 8 cover image IDs (defaults to backgrounds)
74
+ - \`bgOverride\`: array of 6 background IDs to override auto-assignment
75
+ - \`coverOverride\`: array of 6 cover image IDs (defaults to backgrounds)
76
76
  - \`cwd\`: working directory if not process.cwd()
77
77
 
78
78
  ---
79
79
 
80
- ## The 8 cards
80
+ ## The 6 cards
81
81
 
82
82
  Each card = array of detail variants (cover is auto-generated). Provide 1+ variants per card. Quality over quantity: pick only the most impactful, funny, or educational content.
83
83
 
@@ -85,19 +85,12 @@ Each card = array of detail variants (cover is auto-generated). Provide 1+ varia
85
85
  |---|---------|----------------|---------------|------|
86
86
  | 0 | Explain like I'm 5 | 2-3 | img -> title -> text | Playful, zero jargon |
87
87
  | 1 | The roast | 2-3 | img -> title -> text, optional subtext | Maximum cheekiness, swearing ok |
88
- | 2 | Commit message, decoded | 2-3 | social -> title -> text -> optional code | Escalating absurdity |
89
- | 3 | The LinkedIn post | 2-3 | social -> title -> text -> subtext | Peak LinkedIn parody |
90
- | 4 | Statistics | 2-3 | img -> title -> code | Deadpan data humor |
91
- | 5 | Learning moment | 2-3 | img -> title -> text -> optional code -> button(last) | Teach with personality |
92
- | 6 | Alternatives | 2-3 | img -> title -> text -> optional code | Constructive snark |
93
- | 7 | Quiz | 4-6 | title -> quiz (no img blocks) | Quiz show energy |
94
-
95
- ### Card 2 personas (Decoded)
96
- - "What you meant" (source: "x" or "threads")
97
- - "Corporate speak" (source: "linkedin")
98
- - Optional: "Shakespearean" (source: "x"), "Military briefing" (source: "x")
99
-
100
- ### Card 3 personas (LinkedIn)
88
+ | 2 | The LinkedIn post | 2-3 | social -> title -> text | Peak LinkedIn parody |
89
+ | 3 | Learning moment | 2-3 | img -> title -> text -> optional code -> button(last) | Teach with personality |
90
+ | 4 | Alternatives | 2-3 | img -> title -> text -> optional code | Constructive snark |
91
+ | 5 | Quiz | 4-6 | title -> quiz (no img blocks) | Quiz show energy |
92
+
93
+ ### Card 2 personas (LinkedIn)
101
94
  - Chad Gitpush: "10x Engineer | Building in Public | DMs Open"
102
95
  - Alexandra Middleware: "VP of Forward Thinking at TechCorp"
103
96
  - Dave 'Shipping' Johnson: "Just a humble engineer doing humble things"
@@ -106,14 +99,14 @@ Each card = array of detail variants (cover is auto-generated). Provide 1+ varia
106
99
 
107
100
  ## Key rules
108
101
 
109
- - Exactly 8 cards in the cards array (one per section above)
102
+ - Exactly 6 cards in the cards array (one per section above)
110
103
  - Use short image IDs from forkfeed_commits output (img47, not full URLs)
111
104
  - 8-12 unique scene images (img*) across the feed, no duplicate within same card
112
- - Cards 0-5: code must be REAL from the diff (never fabricated)
113
- - Card 6: synthesized code IS allowed
114
- - Card 7: no img blocks, quiz blocks only (with title per variant)
115
- - Card 5: every detail variant MUST end with a button block
116
- - Cards 2-3: first block should be social (not img)
105
+ - Cards 0-3: code must be REAL from the diff (never fabricated)
106
+ - Card 4: synthesized code IS allowed
107
+ - Card 5: no img blocks, quiz blocks only (with title per variant)
108
+ - Card 3: every detail variant MUST end with a button block
109
+ - Card 2: first block should be social (not img), no engagement metrics subtext (reactions, comments, reposts)
117
110
  - CONTENT_QUIZ: 4 options recommended (min 2), exactly one correct, always include explanation
118
111
  - No em dashes, smart quotes, or non-ASCII characters
119
112
  - Feed title max 60 chars
package/dist/index.js CHANGED
@@ -218,14 +218,12 @@ function parseFilePathsFromStats(stats) {
218
218
  const BG_PREFS = [
219
219
  ['bg10', 'bg27'], // 0: ELI5
220
220
  ['bg11', 'bg1'], // 1: Roast
221
- ['bg20', 'bg11'], // 2: Decoded
222
- ['bg25', 'bg24'], // 3: LinkedIn
223
- ['bg9', 'bg3'], // 4: Stats
224
- ['bg12', 'bg13', 'bg14', 'bg15', 'bg17'], // 5: Learning (tech-match)
225
- ['bg25', 'bg30'], // 6: Alternatives
226
- ['bg18', 'bg5'], // 7: Quiz
221
+ ['bg25', 'bg24'], // 2: LinkedIn
222
+ ['bg12', 'bg13', 'bg14', 'bg15', 'bg17'], // 3: Learning (tech-match)
223
+ ['bg25', 'bg30'], // 4: Alternatives
224
+ ['bg18', 'bg5'], // 5: Quiz
227
225
  ];
228
- // Map language tags to preferred Learning card (index 5) backgrounds
226
+ // Map language tags to preferred Learning card (index 3) backgrounds
229
227
  const LANG_BG = {
230
228
  javascript: 'bg12', typescript: 'bg12',
231
229
  python: 'bg13',
@@ -235,8 +233,8 @@ const LANG_BG = {
235
233
  };
236
234
  function assignBackgrounds(tags) {
237
235
  const used = new Set();
238
- // For card 5 (Learning), reorder prefs based on detected language tags
239
- const learningPrefs = [...BG_PREFS[5]];
236
+ // For card 3 (Learning), reorder prefs based on detected language tags
237
+ const learningPrefs = [...BG_PREFS[3]];
240
238
  for (const tag of tags) {
241
239
  const preferred = LANG_BG[tag];
242
240
  if (preferred && learningPrefs.includes(preferred)) {
@@ -247,7 +245,7 @@ function assignBackgrounds(tags) {
247
245
  }
248
246
  }
249
247
  return BG_PREFS.map((prefs, i) => {
250
- const candidates = i === 5 ? learningPrefs : prefs;
248
+ const candidates = i === 3 ? learningPrefs : prefs;
251
249
  const pick = candidates.find((bg) => !used.has(bg)) || candidates[0];
252
250
  used.add(pick);
253
251
  return pick;
@@ -286,9 +284,7 @@ async function fetchStatusData() {
286
284
  const COVERS = [
287
285
  { title: "Explain Like I'm 5", subtitle: "Hopefully now you'll understand what you pushed" },
288
286
  { title: "The Roast", subtitle: "Your code had it coming" },
289
- { title: "Commit Message, Decoded", subtitle: "What you wrote vs what you meant vs what actually happened" },
290
287
  { title: "The LinkedIn Post", subtitle: "Mass cringe, freshly generated" },
291
- { title: "Statistics", subtitle: "The numbers don't lie, but they do judge" },
292
288
  { title: "Learning Moment", subtitle: "Something useful buried in your chaos" },
293
289
  { title: "Alternatives", subtitle: "What you could have done instead" },
294
290
  { title: "Quiz", subtitle: "Let's see if you even understand your own code" },
@@ -358,10 +354,10 @@ const buildInputSchema = z.object({
358
354
  variants: z.array(z.object({
359
355
  blocks: z.array(z.record(z.string(), z.unknown())).min(1),
360
356
  })).min(1),
361
- })).length(8),
357
+ })).length(6),
362
358
  cwd: z.string().optional().describe('Working directory (defaults to process.cwd())'),
363
- bgOverride: z.array(z.string()).length(8).optional().describe('Override auto-assigned background IDs'),
364
- coverOverride: z.array(z.string()).length(8).optional().describe('Override cover image IDs (defaults to backgrounds)'),
359
+ bgOverride: z.array(z.string()).length(6).optional().describe('Override auto-assigned background IDs'),
360
+ coverOverride: z.array(z.string()).length(6).optional().describe('Override cover image IDs (defaults to backgrounds)'),
365
361
  });
366
362
  // ── Manifest builder ─────────────────────────────────────────────────
367
363
  async function buildManifest(input) {
@@ -407,7 +403,7 @@ async function buildManifest(input) {
407
403
  title: input.forkTitle,
408
404
  description: input.forkDescription,
409
405
  imageSrc: coverUrls[0],
410
- feedIds: [...existingFeedIds, feedId],
406
+ feedIds: [feedId, ...existingFeedIds],
411
407
  ...(actionUrl ? { actionLabel: 'View on GitHub', actionUrl } : {}),
412
408
  };
413
409
  const feed = {
@@ -477,9 +473,11 @@ async function pushManifestToServer(manifest) {
477
473
  const forkSummary = data.forks
478
474
  ?.map((f) => ` - ${f.title} (${f.feeds} feeds)`)
479
475
  .join('\n');
476
+ // Use the real MongoDB ObjectId returned by the server, not the manifest slug
477
+ const realForkId = data.forks?.[0]?.forkId || forkId;
480
478
  let qrBlock = '';
481
- if (forkId) {
482
- const url = `https://forkfeed.link/fork/${forkId}`;
479
+ if (realForkId) {
480
+ const url = `https://forkfeed.link/fork/${realForkId}`;
483
481
  try {
484
482
  const qr = await QRCode.toString(url, { type: 'utf8', errorCorrectionLevel: 'L' });
485
483
  qrBlock = ['', 'Scan to open in forkfeed:', '', qr].join('\n');
@@ -648,7 +646,7 @@ server.tool('forkfeed_push', 'Push a generated manifest (forks, feeds, cards) to
648
646
  });
649
647
  // ── Tool: forkfeed_build ──────────────────────────────────────────────
650
648
  server.tool('forkfeed_build', 'Build a forkfeed manifest from simplified content and push it. Auto-detects repo info, assigns backgrounds, fetches existing feeds. Use this instead of forkfeed_push.', {
651
- content: buildInputSchema.describe('Simplified content: sha, titles, descriptions, and 8 cards with blocks'),
649
+ content: buildInputSchema.describe('Simplified content: sha, titles, descriptions, and 6 cards with blocks'),
652
650
  push: z.boolean().optional().describe('Push immediately after building (default: true)'),
653
651
  }, async ({ content, push }) => {
654
652
  const shouldPush = push !== false;
@@ -744,7 +742,7 @@ server.tool('forkfeed_status', 'Check your current forkfeed content: which forks
744
742
  }
745
743
  });
746
744
  // ── Prompt: /forkfeed ──────────────────────────────────────────────────
747
- server.prompt('forkfeed', 'Turn GitHub commits into swipeable forkfeed content. Analyzes your repo, generates 8-card feeds, and pushes them live.', async () => ({
745
+ server.prompt('forkfeed', 'Turn GitHub commits into swipeable forkfeed content. Analyzes your repo, generates 6-card feeds, and pushes them live.', async () => ({
748
746
  messages: [
749
747
  {
750
748
  role: 'user',
@@ -755,7 +753,7 @@ server.prompt('forkfeed', 'Turn GitHub commits into swipeable forkfeed content.
755
753
  1. Call **forkfeed_guide** and **forkfeed_commits()** in parallel (no arguments for commits = list mode).
756
754
  2. Show the commits table from forkfeed_commits. It already indicates which commits have published feeds. Ask which ONE commit to process. One commit at a time, never more. Do NOT ask about image style.
757
755
  3. Call **forkfeed_commits** with the selected commit SHA. This returns the diff, file stats, and suggested scene images. Do NOT run git commands yourself.
758
- 4. Generate the simplified content JSON: sha, feedTitle, feedDescription, forkTitle, forkDescription, and 8 cards with blocks. Use short image IDs (img47) for inline images. Do NOT provide owner, repo, backgrounds, existingFeedIds, UUIDs, covers, or type wrappers. The builder auto-detects and generates all of that.
756
+ 4. Generate the simplified content JSON: sha, feedTitle, feedDescription, forkTitle, forkDescription, and 6 cards with blocks. Use short image IDs (img47) for inline images. Do NOT provide owner, repo, backgrounds, existingFeedIds, UUIDs, covers, or type wrappers. The builder auto-detects and generates all of that.
759
757
  5. Call **forkfeed_build** with the simplified content (push defaults to true).
760
758
  6. Display the QR code block from the push result exactly as returned, so the user can scan it.
761
759
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forkfeed-mcp",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "description": "MCP server for pushing GitHub commits to forkfeed",
5
5
  "type": "module",
6
6
  "bin": {