forkfeed-mcp 1.2.0 → 1.3.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.js +21 -37
- package/dist/index.js +78 -53
- package/package.json +1 -1
package/dist/guide-content.js
CHANGED
|
@@ -14,7 +14,7 @@ Turn GitHub commits into swipeable card content. One fork per repo, one feed per
|
|
|
14
14
|
|
|
15
15
|
1. Call **forkfeed_guide** and **forkfeed_commits()** in parallel
|
|
16
16
|
2. Show commits table, ask which ONE to process
|
|
17
|
-
3. Call **forkfeed_commits(sha)** to get diff, stats, and
|
|
17
|
+
3. Call **forkfeed_commits(sha)** to get diff, stats, and suggested scene images
|
|
18
18
|
4. Generate simplified content JSON (see format below)
|
|
19
19
|
5. Call **forkfeed_build** with the content (push defaults to true)
|
|
20
20
|
|
|
@@ -22,26 +22,19 @@ Turn GitHub commits into swipeable card content. One fork per repo, one feed per
|
|
|
22
22
|
|
|
23
23
|
## Simplified content format (for forkfeed_build)
|
|
24
24
|
|
|
25
|
-
The builder
|
|
25
|
+
The builder auto-detects repo info (owner, repo, GitHub URL), auto-assigns card backgrounds, and fetches existing feed IDs from the server. You only provide creative content.
|
|
26
26
|
|
|
27
27
|
\`\`\`json
|
|
28
28
|
{
|
|
29
|
-
"
|
|
30
|
-
"repo": "repo-name",
|
|
31
|
-
"sha": "7char",
|
|
29
|
+
"sha": "7char-sha",
|
|
32
30
|
"feedTitle": "Max 60 chars headline",
|
|
33
31
|
"feedDescription": "Mon DD: one-line impact summary",
|
|
34
32
|
"forkTitle": "Project Name",
|
|
35
33
|
"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
|
-
},
|
|
41
34
|
"cards": [
|
|
42
35
|
{
|
|
43
36
|
"variants": [
|
|
44
|
-
{ "blocks": [{ "img": "img47"
|
|
37
|
+
{ "blocks": [{ "img": "img47" }, { "title": "Section heading" }, { "text": "Paragraph content..." }] },
|
|
45
38
|
{ "blocks": [{ "title": "Another section" }, { "text": "More content..." }] }
|
|
46
39
|
]
|
|
47
40
|
}
|
|
@@ -49,6 +42,8 @@ The builder handles UUIDs, full image URLs, cover variants, type wrappers, feedI
|
|
|
49
42
|
}
|
|
50
43
|
\`\`\`
|
|
51
44
|
|
|
45
|
+
Only 5 fields + cards. Everything else is auto-populated.
|
|
46
|
+
|
|
52
47
|
### Block types (inferred from fields)
|
|
53
48
|
|
|
54
49
|
| Write this | Becomes | Notes |
|
|
@@ -59,19 +54,27 @@ The builder handles UUIDs, full image URLs, cover variants, type wrappers, feedI
|
|
|
59
54
|
| \`{ "text": "Paragraph..." }\` | CONTENT_TEXT | 50-200 words, use \\\\n\\\\n for breaks |
|
|
60
55
|
| \`{ "code": "const x = 1", "lang": "typescript" }\` | CONTENT_CODE | Real code from diff, 5-20 lines, strip +/- prefixes |
|
|
61
56
|
| \`{ "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. |
|
|
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. |
|
|
63
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 |
|
|
64
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 |
|
|
65
60
|
|
|
66
61
|
### What the builder does automatically
|
|
62
|
+
- Detects repo owner/name from git remote
|
|
63
|
+
- Generates fork/feed IDs from convention (tfip-{owner}-{repo}-{sha})
|
|
64
|
+
- Constructs GitHub action URL
|
|
65
|
+
- Fetches existing feed IDs from server (for incremental updates)
|
|
66
|
+
- Assigns 8 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
|
|
70
|
-
- Sets backgroundSrc
|
|
71
|
-
- Sets feedId and order on each card
|
|
72
|
-
- Builds fork and feed objects with correct IDs and metadata
|
|
70
|
+
- Sets backgroundSrc, feedId, and order on each card
|
|
73
71
|
- Validates the assembled manifest before pushing
|
|
74
72
|
|
|
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)
|
|
76
|
+
- \`cwd\`: working directory if not process.cwd()
|
|
77
|
+
|
|
75
78
|
---
|
|
76
79
|
|
|
77
80
|
## The 8 cards
|
|
@@ -104,7 +107,7 @@ Each card = array of detail variants (cover is auto-generated). Provide 1+ varia
|
|
|
104
107
|
## Key rules
|
|
105
108
|
|
|
106
109
|
- Exactly 8 cards in the cards array (one per section above)
|
|
107
|
-
-
|
|
110
|
+
- Use short image IDs from forkfeed_commits output (img47, not full URLs)
|
|
108
111
|
- 12-20 unique scene images (img*) across the feed, no duplicate within same card
|
|
109
112
|
- Cards 0-5: code must be REAL from the diff (never fabricated)
|
|
110
113
|
- Card 6: synthesized code IS allowed
|
|
@@ -115,32 +118,13 @@ Each card = array of detail variants (cover is auto-generated). Provide 1+ varia
|
|
|
115
118
|
- No em dashes, smart quotes, or non-ASCII characters
|
|
116
119
|
- Feed title max 60 chars
|
|
117
120
|
|
|
118
|
-
### Images
|
|
119
|
-
|
|
120
|
-
Images are pre-filtered by **forkfeed_commits(sha)**. Pick from the suggested list.
|
|
121
|
-
|
|
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
|
|
123
|
-
|
|
124
121
|
### Tone
|
|
125
122
|
Casual, cheeky, technically accurate. Use "you," contractions, short paragraphs, occasional swearing. Humor is the default.
|
|
126
123
|
|
|
127
124
|
### Incremental updates
|
|
128
|
-
**forkfeed_commits()** (list mode) shows which commits have published feeds
|
|
125
|
+
**forkfeed_commits()** (list mode) shows which commits have published feeds. The builder auto-fetches existing feed IDs, so you don't need to track them.
|
|
129
126
|
|
|
130
|
-
- **New commit**:
|
|
127
|
+
- **New commit**: Just generate content. The builder handles feed ID preservation.
|
|
131
128
|
- **Existing commit**: Warn the user. Only regenerate if they confirm.
|
|
132
129
|
- **One commit per run**: Never process multiple commits at once.
|
|
133
|
-
|
|
134
|
-
---
|
|
135
|
-
|
|
136
|
-
## Advanced: direct push with full manifest (forkfeed_push)
|
|
137
|
-
|
|
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
|
|
143
|
-
|
|
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).
|
|
146
130
|
`.trim();
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import QRCode from 'qrcode';
|
|
10
10
|
import { GUIDE_CONTENT } from './guide-content.js';
|
|
11
|
-
import { IMAGE_CATALOG,
|
|
11
|
+
import { IMAGE_CATALOG, resolveImageId } from './image-catalog.js';
|
|
12
12
|
const execFileAsync = promisify(execFile);
|
|
13
13
|
const APP_SERVER_URL = (process.env.APP_SERVER_URL || 'https://api.forkfeed.link').replace(/\/+$/, '');
|
|
14
14
|
const TOKEN = process.env.FORKFEED_TOKEN || '';
|
|
@@ -136,11 +136,12 @@ async function getRepoInfo(cwd) {
|
|
|
136
136
|
catch { /* no remote */ }
|
|
137
137
|
return { owner: null, repo: basename(cwd), isLocal: true };
|
|
138
138
|
}
|
|
139
|
+
const SEP = '\x1e'; // ASCII record separator (safe in commit messages)
|
|
139
140
|
async function getCommitList(cwd, count = 15) {
|
|
140
|
-
const fmt =
|
|
141
|
+
const fmt = `%H${SEP}%h${SEP}%s${SEP}%an${SEP}%aI`;
|
|
141
142
|
const raw = await gitExec(['log', '--no-merges', `-${count}`, `--format=${fmt}`], cwd);
|
|
142
143
|
return raw.trim().split('\n').filter(Boolean).map((line) => {
|
|
143
|
-
const [sha, shortSha, message, author, date] = line.split(
|
|
144
|
+
const [sha, shortSha, message, author, date] = line.split(SEP);
|
|
144
145
|
return { sha, shortSha, message, author, date: date?.slice(0, 10) || '' };
|
|
145
146
|
});
|
|
146
147
|
}
|
|
@@ -209,6 +210,28 @@ function detectTags(message, filePaths) {
|
|
|
209
210
|
tags.add('general');
|
|
210
211
|
return [...tags];
|
|
211
212
|
}
|
|
213
|
+
function parseFilePathsFromStats(stats) {
|
|
214
|
+
const matches = stats.match(/^\s*(.+?)\s+\|/gm) || [];
|
|
215
|
+
return matches.map((m) => m.trim().replace(/\s+\|$/, ''));
|
|
216
|
+
}
|
|
217
|
+
const BG_PREFS = [
|
|
218
|
+
['bg10', 'bg27'], // 0: ELI5
|
|
219
|
+
['bg11', 'bg1'], // 1: Roast
|
|
220
|
+
['bg20', 'bg11'], // 2: Decoded
|
|
221
|
+
['bg25', 'bg24'], // 3: LinkedIn
|
|
222
|
+
['bg9', 'bg3'], // 4: Stats
|
|
223
|
+
['bg12', 'bg13', 'bg14', 'bg15', 'bg17'], // 5: Learning (tech-match)
|
|
224
|
+
['bg25', 'bg30'], // 6: Alternatives
|
|
225
|
+
['bg18', 'bg5'], // 7: Quiz
|
|
226
|
+
];
|
|
227
|
+
function assignBackgrounds(_tags) {
|
|
228
|
+
const used = new Set();
|
|
229
|
+
return BG_PREFS.map((prefs) => {
|
|
230
|
+
const pick = prefs.find((bg) => !used.has(bg)) || prefs[0];
|
|
231
|
+
used.add(pick);
|
|
232
|
+
return pick;
|
|
233
|
+
});
|
|
234
|
+
}
|
|
212
235
|
function filterImagesByTags(tags) {
|
|
213
236
|
const tagSet = new Set(tags);
|
|
214
237
|
function score(entry) {
|
|
@@ -219,13 +242,13 @@ function filterImagesByTags(tags) {
|
|
|
219
242
|
.map((i) => ({ ...i, score: score(i) }))
|
|
220
243
|
.sort((a, b) => b.score - a.score)
|
|
221
244
|
.slice(0, 30)
|
|
222
|
-
.map((i) => `${i.id} | ${i.name} | ${i.tags}
|
|
245
|
+
.map((i) => `${i.id} | ${i.name} | ${i.tags}`);
|
|
223
246
|
const backgrounds = IMAGE_CATALOG
|
|
224
247
|
.filter((i) => i.id.startsWith('bg'))
|
|
225
248
|
.map((i) => ({ ...i, score: score(i) }))
|
|
226
249
|
.sort((a, b) => b.score - a.score)
|
|
227
250
|
.slice(0, 10)
|
|
228
|
-
.map((i) => `${i.id} | ${i.name} | ${i.tags}
|
|
251
|
+
.map((i) => `${i.id} | ${i.name} | ${i.tags}`);
|
|
229
252
|
return { scenes, backgrounds };
|
|
230
253
|
}
|
|
231
254
|
async function fetchStatusData() {
|
|
@@ -260,10 +283,11 @@ function inferBlock(block) {
|
|
|
260
283
|
};
|
|
261
284
|
}
|
|
262
285
|
if ('name' in block && 'avatar' in block) {
|
|
286
|
+
const avatar = block.avatar;
|
|
263
287
|
return {
|
|
264
288
|
type: 'CONTENT_SOCIAL',
|
|
265
289
|
name: block.name,
|
|
266
|
-
avatarSrc: resolveImageId(
|
|
290
|
+
avatarSrc: avatar.startsWith('http') ? avatar : resolveImageId(avatar),
|
|
267
291
|
source: block.source,
|
|
268
292
|
...(block.subtitle != null ? { subtitle: block.subtitle } : {}),
|
|
269
293
|
};
|
|
@@ -303,41 +327,66 @@ function inferBlock(block) {
|
|
|
303
327
|
}
|
|
304
328
|
// ── Build input schema ───────────────────────────────────────────────
|
|
305
329
|
const buildInputSchema = z.object({
|
|
306
|
-
owner: z.string().min(1),
|
|
307
|
-
repo: z.string().min(1),
|
|
308
330
|
sha: z.string().min(1),
|
|
309
331
|
feedTitle: z.string().min(1).max(60),
|
|
310
332
|
feedDescription: z.string().min(1),
|
|
311
333
|
forkTitle: z.string().min(1),
|
|
312
334
|
forkDescription: z.string().min(1),
|
|
313
|
-
actionUrl: z.string().optional(),
|
|
314
|
-
existingFeedIds: z.array(z.string()).optional(),
|
|
315
|
-
images: z.object({
|
|
316
|
-
bg: z.array(z.string()).length(8),
|
|
317
|
-
cover: z.array(z.string()).length(8).optional(),
|
|
318
|
-
}),
|
|
319
335
|
cards: z.array(z.object({
|
|
320
336
|
variants: z.array(z.object({
|
|
321
337
|
blocks: z.array(z.record(z.string(), z.unknown())).min(1),
|
|
322
338
|
})).min(1),
|
|
323
339
|
})).length(8),
|
|
340
|
+
cwd: z.string().optional().describe('Working directory (defaults to process.cwd())'),
|
|
341
|
+
bgOverride: z.array(z.string()).length(8).optional().describe('Override auto-assigned background IDs'),
|
|
342
|
+
coverOverride: z.array(z.string()).length(8).optional().describe('Override cover image IDs (defaults to backgrounds)'),
|
|
324
343
|
});
|
|
325
344
|
// ── Manifest builder ─────────────────────────────────────────────────
|
|
326
|
-
function buildManifest(input) {
|
|
327
|
-
const
|
|
328
|
-
|
|
345
|
+
async function buildManifest(input) {
|
|
346
|
+
const cwd = input.cwd || process.cwd();
|
|
347
|
+
// Auto-detect repo info
|
|
348
|
+
const repoInfo = await getRepoInfo(cwd);
|
|
349
|
+
const owner = repoInfo.owner || 'local';
|
|
350
|
+
const repo = repoInfo.repo;
|
|
351
|
+
const sha7 = input.sha.slice(0, 7);
|
|
352
|
+
const forkId = `tfip-${owner}-${repo}`;
|
|
353
|
+
const feedId = `tfip-${owner}-${repo}-${sha7}`;
|
|
354
|
+
// Auto-assign backgrounds (or use override)
|
|
355
|
+
let bgs;
|
|
356
|
+
if (input.bgOverride) {
|
|
357
|
+
bgs = input.bgOverride;
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
const meta = await gitExec(['show', input.sha, '--format=%s', '--no-patch'], cwd);
|
|
361
|
+
const statOutput = await gitExec(['diff', `${input.sha}^..${input.sha}`, '--stat'], cwd);
|
|
362
|
+
const filePaths = parseFilePathsFromStats(statOutput);
|
|
363
|
+
const tags = detectTags(meta.trim(), filePaths);
|
|
364
|
+
bgs = assignBackgrounds(tags);
|
|
365
|
+
}
|
|
366
|
+
// Auto-fetch existing feed IDs (best-effort)
|
|
367
|
+
let existingFeedIds = [];
|
|
368
|
+
if (TOKEN) {
|
|
369
|
+
try {
|
|
370
|
+
const status = await fetchStatusData();
|
|
371
|
+
existingFeedIds = (status.feeds || []).map((f) => f.externalFeedId);
|
|
372
|
+
}
|
|
373
|
+
catch { /* best-effort */ }
|
|
374
|
+
}
|
|
329
375
|
// Resolve image IDs
|
|
330
|
-
const bgUrls =
|
|
331
|
-
const coverUrls = input.
|
|
332
|
-
? input.
|
|
376
|
+
const bgUrls = bgs.map(resolveImageId);
|
|
377
|
+
const coverUrls = input.coverOverride
|
|
378
|
+
? input.coverOverride.map(resolveImageId)
|
|
333
379
|
: bgUrls;
|
|
380
|
+
const actionUrl = repoInfo.owner
|
|
381
|
+
? `https://github.com/${repoInfo.owner}/${repoInfo.repo}`
|
|
382
|
+
: undefined;
|
|
334
383
|
const fork = {
|
|
335
384
|
_id: forkId,
|
|
336
385
|
title: input.forkTitle,
|
|
337
386
|
description: input.forkDescription,
|
|
338
387
|
imageSrc: coverUrls[0],
|
|
339
|
-
feedIds: [...
|
|
340
|
-
...(
|
|
388
|
+
feedIds: [...existingFeedIds, feedId],
|
|
389
|
+
...(actionUrl ? { actionLabel: 'View on GitHub', actionUrl } : {}),
|
|
341
390
|
};
|
|
342
391
|
const feed = {
|
|
343
392
|
_id: feedId,
|
|
@@ -548,30 +597,6 @@ server.tool('forkfeed_commits', 'Read git commits from the current repo. Without
|
|
|
548
597
|
].filter(Boolean);
|
|
549
598
|
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
550
599
|
});
|
|
551
|
-
// ── Tool: forkfeed_images ──────────────────────────────────────────────
|
|
552
|
-
server.tool('forkfeed_images', 'Get the IT Scenes image catalog (200 scene images + 30 backgrounds). Use this to pick images that match your content by tags and name. Call after forkfeed_guide.', {}, async () => {
|
|
553
|
-
const scenes = IMAGE_CATALOG.filter((i) => i.id.startsWith('img'));
|
|
554
|
-
const bgs = IMAGE_CATALOG.filter((i) => i.id.startsWith('bg'));
|
|
555
|
-
const lines = [
|
|
556
|
-
'# IT Scenes Image Catalog',
|
|
557
|
-
'',
|
|
558
|
-
`Base URL: ${IMAGE_BASE_URL}`,
|
|
559
|
-
'Prepend base URL to all filenames below to get the full image URL.',
|
|
560
|
-
'',
|
|
561
|
-
'Match images to content by tags and name. Use img* for covers and inline images, bg* for card backgrounds.',
|
|
562
|
-
'',
|
|
563
|
-
'Tags: deploy, git, disaster, debug, hype, victory, beginner, language, lifestyle, workplace, sarcastic, general',
|
|
564
|
-
'',
|
|
565
|
-
'## Scene Images (covers + inline)',
|
|
566
|
-
'',
|
|
567
|
-
...scenes.map((i) => `${i.id} | ${i.name} | ${i.tags} | ${i.file}`),
|
|
568
|
-
'',
|
|
569
|
-
'## Background Images',
|
|
570
|
-
'',
|
|
571
|
-
...bgs.map((i) => `${i.id} | ${i.name} | ${i.tags} | ${i.file}`),
|
|
572
|
-
];
|
|
573
|
-
return { content: [{ type: 'text', text: lines.join('\n') }] };
|
|
574
|
-
});
|
|
575
600
|
// ── Tool: forkfeed_push ────────────────────────────────────────────────
|
|
576
601
|
server.tool('forkfeed_push', 'Push a generated manifest (forks, feeds, cards) to forkfeed. The manifest JSON must conform to the structure described in forkfeed_guide.', {
|
|
577
602
|
manifest: z
|
|
@@ -603,15 +628,15 @@ server.tool('forkfeed_push', 'Push a generated manifest (forks, feeds, cards) to
|
|
|
603
628
|
return pushManifestToServer(parsed.data);
|
|
604
629
|
});
|
|
605
630
|
// ── Tool: forkfeed_build ──────────────────────────────────────────────
|
|
606
|
-
server.tool('forkfeed_build', 'Build a forkfeed manifest from simplified content and push it.
|
|
607
|
-
content: buildInputSchema.describe('Simplified content:
|
|
631
|
+
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.', {
|
|
632
|
+
content: buildInputSchema.describe('Simplified content: sha, titles, descriptions, and 8 cards with blocks'),
|
|
608
633
|
push: z.boolean().optional().describe('Push immediately after building (default: true)'),
|
|
609
634
|
}, async ({ content, push }) => {
|
|
610
635
|
const shouldPush = push !== false;
|
|
611
636
|
// Build the full manifest from simplified input
|
|
612
637
|
let manifest;
|
|
613
638
|
try {
|
|
614
|
-
manifest = buildManifest(content);
|
|
639
|
+
manifest = await buildManifest(content);
|
|
615
640
|
}
|
|
616
641
|
catch (err) {
|
|
617
642
|
return {
|
|
@@ -665,7 +690,7 @@ server.tool('forkfeed_status', 'Check your current forkfeed content: which forks
|
|
|
665
690
|
content: [
|
|
666
691
|
{
|
|
667
692
|
type: 'text',
|
|
668
|
-
text: 'No forks published yet. Use forkfeed_guide to learn how to generate content, then push it with
|
|
693
|
+
text: 'No forks published yet. Use forkfeed_guide to learn how to generate content, then push it with forkfeed_build.',
|
|
669
694
|
},
|
|
670
695
|
],
|
|
671
696
|
};
|
|
@@ -708,9 +733,9 @@ server.prompt('forkfeed', 'Turn GitHub commits into swipeable forkfeed content.
|
|
|
708
733
|
text: `Turn the commits in this repo into forkfeed content. Follow these steps exactly:
|
|
709
734
|
|
|
710
735
|
1. Call **forkfeed_guide** and **forkfeed_commits()** in parallel (no arguments for commits = list mode).
|
|
711
|
-
2. Show the commits table from forkfeed_commits. It already indicates which commits have published feeds
|
|
712
|
-
3. Call **forkfeed_commits** with the selected commit SHA. This returns the diff, file stats, and
|
|
713
|
-
4. Generate the simplified content JSON
|
|
736
|
+
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.
|
|
737
|
+
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.
|
|
738
|
+
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.
|
|
714
739
|
5. Call **forkfeed_build** with the simplified content (push defaults to true).
|
|
715
740
|
|
|
716
741
|
Start now.`,
|