@gobing-ai/knowledge-kit 0.0.26 → 0.0.28
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/.claude-plugin/marketplace.json +1 -1
- package/package.json +1 -1
- package/plugins/generations/dailynews-gen/dist/index.js +15 -12
- package/plugins/generations/dailynews-gen/src/script-builder.ts +18 -9
- package/plugins/generations/episode-plan-gen/dist/index.js +7 -0
- package/plugins/generations/episode-plan-gen/src/index.ts +12 -0
- package/plugins/generations/image-gen/dist/index.js +6 -1
- package/plugins/generations/image-gen/src/providers/google.ts +9 -1
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/scripts/kk-workflow-stages.ts +77 -3
- package/plugins/kk/workflows/kk-daily-ai-voice.yaml +19 -7
- package/plugins/publishings/wechatmp-pub/dist/index.js +272 -115
- package/plugins/publishings/wechatmp-pub/package.json +2 -1
- package/plugins/publishings/wechatmp-pub/src/index.ts +336 -40
- package/plugins/publishings/wechatmp-pub/src/profile-check.ts +21 -9
- package/plugins/publishings/xhs-pub/dist/index.js +28 -22
- package/plugins/publishings/xhs-pub/package.json +2 -1
- package/plugins/publishings/xhs-pub/src/index.ts +30 -22
package/package.json
CHANGED
|
@@ -22064,19 +22064,22 @@ function stripLeadingTitleEcho(body, title) {
|
|
|
22064
22064
|
const wanted = title.replace(TITLE_ECHO_NOISE_RE, "");
|
|
22065
22065
|
if (wanted.length < 4)
|
|
22066
22066
|
return body;
|
|
22067
|
-
let
|
|
22068
|
-
|
|
22069
|
-
|
|
22070
|
-
|
|
22071
|
-
|
|
22072
|
-
|
|
22073
|
-
|
|
22074
|
-
|
|
22075
|
-
|
|
22067
|
+
let text = body;
|
|
22068
|
+
for (;; ) {
|
|
22069
|
+
let matched = "";
|
|
22070
|
+
let end = -1;
|
|
22071
|
+
for (let i = 0;i < text.length && matched.length < wanted.length; i++) {
|
|
22072
|
+
const kept = (text[i] ?? "").replace(TITLE_ECHO_NOISE_RE, "");
|
|
22073
|
+
if (kept === "")
|
|
22074
|
+
continue;
|
|
22075
|
+
matched += kept;
|
|
22076
|
+
if (matched.length >= wanted.length)
|
|
22077
|
+
end = i + 1;
|
|
22078
|
+
}
|
|
22079
|
+
if (end === -1 || matched !== wanted)
|
|
22080
|
+
return text;
|
|
22081
|
+
text = text.slice(end).replace(/^[\uFF0C\u3002\u3001\uFF1A:\uFF1B;!\uFF01?\uFF1F\u2026\s\u300C\u300D\u300E\u300F\u3010\u3011]+/, "");
|
|
22076
22082
|
}
|
|
22077
|
-
if (end === -1 || matched !== wanted)
|
|
22078
|
-
return body;
|
|
22079
|
-
return body.slice(end).replace(/^[\uFF0C\u3002\u3001\uFF1A:\uFF1B;!\uFF01?\uFF1F\u2026\s\u300C\u300D\u300E\u300F\u3010\u3011]+/, "");
|
|
22080
22083
|
}
|
|
22081
22084
|
var TAG_METADATA_LINE_RE = /^\s*\**\s*\u6807\u7B7E\s*\**\s*[\uFF1A:\uFF0C,]/;
|
|
22082
22085
|
function stripTagMetadataLines(body) {
|
|
@@ -232,20 +232,29 @@ const TITLE_ECHO_NOISE_RE = /[,。、::;;!!??…\s「」『』【】]/
|
|
|
232
232
|
* character prefix compare, never a fuzzy/LLM similarity match (anti-pattern list). The body is
|
|
233
233
|
* returned untouched whenever the normalized prefix does not equal the normalized title, and a
|
|
234
234
|
* body that is *only* the restatement collapses to the empty string (the lead alone carries it).
|
|
235
|
+
*
|
|
236
|
+
* 20260929 dogfood: plan bodies can restate the title MORE THAN once — a bare first line plus a
|
|
237
|
+
* restated line carrying a parenthetical extension (`title(来源标注日期为…)`). A single pass
|
|
238
|
+
* strips only the first, so the second becomes the new head and the VoiceScript validator rejects
|
|
239
|
+
* the segment. Keep stripping until the head no longer echoes; every accepted pass strictly
|
|
240
|
+
* shortens the body, so the loop terminates.
|
|
235
241
|
*/
|
|
236
242
|
export function stripLeadingTitleEcho(body: string, title: string): string {
|
|
237
243
|
const wanted = title.replace(TITLE_ECHO_NOISE_RE, '');
|
|
238
244
|
if (wanted.length < 4) return body; // a short title would match too much of the body
|
|
239
|
-
let
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
245
|
+
let text = body;
|
|
246
|
+
for (;;) {
|
|
247
|
+
let matched = '';
|
|
248
|
+
let end = -1;
|
|
249
|
+
for (let i = 0; i < text.length && matched.length < wanted.length; i++) {
|
|
250
|
+
const kept = (text[i] ?? '').replace(TITLE_ECHO_NOISE_RE, '');
|
|
251
|
+
if (kept === '') continue; // drift punctuation between title and echo
|
|
252
|
+
matched += kept;
|
|
253
|
+
if (matched.length >= wanted.length) end = i + 1;
|
|
254
|
+
}
|
|
255
|
+
if (end === -1 || matched !== wanted) return text;
|
|
256
|
+
text = text.slice(end).replace(/^[,。、::;;!!??…\s「」『』【】]+/, '');
|
|
246
257
|
}
|
|
247
|
-
if (end === -1 || matched !== wanted) return body;
|
|
248
|
-
return body.slice(end).replace(/^[,。、::;;!!??…\s「」『』【】]+/, '');
|
|
249
258
|
}
|
|
250
259
|
|
|
251
260
|
/**
|
|
@@ -22101,7 +22101,14 @@ function docTimestamp(doc2) {
|
|
|
22101
22101
|
}
|
|
22102
22102
|
return null;
|
|
22103
22103
|
}
|
|
22104
|
+
var CHINA_POLITICS_RE = /\u4E60\u8FD1\u5E73|\u674E\u514B\u5F3A|\u674E\u5F3A|\u515A\u4E2D\u592E|\u653F\u6CBB\u5C40|\u4E2D\u5357\u6D77|\u653F\u5E9C\u5DE5\u4F5C\u62A5\u544A|\u56FD\u53F0\u529E|\u5916\u4EA4\u90E8\u53D1\u8A00\u4EBA|\u4E24\u4F1A/;
|
|
22105
|
+
function isChinaPolitics(doc2) {
|
|
22106
|
+
return CHINA_POLITICS_RE.test(`${doc2.title ?? ""}
|
|
22107
|
+
${(doc2.body ?? "").slice(0, 2000)}`);
|
|
22108
|
+
}
|
|
22104
22109
|
function passesGates(doc2, planDate) {
|
|
22110
|
+
if (isChinaPolitics(doc2))
|
|
22111
|
+
return false;
|
|
22105
22112
|
const meta3 = asRecord(doc2.metadata);
|
|
22106
22113
|
const tags = Array.isArray(meta3.tags) ? meta3.tags.filter((t) => typeof t === "string") : [];
|
|
22107
22114
|
if (tags.some((tag) => NON_NEWS_TAGS.has(tag.toLowerCase())))
|
|
@@ -227,7 +227,19 @@ function docTimestamp(doc: Doc): number | null {
|
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
/** Freshness + non-news gates applied before scoring/dedup (archived or off-topic items never enter the pool). */
|
|
230
|
+
/**
|
|
231
|
+
* Platform-safety gate (operator rule 2026-09-29): China domestic politics stories never enter
|
|
232
|
+
* the plan. Keyword heuristic — high-precision markers + top-leader names only; widen after
|
|
233
|
+
* reviewing false negatives in the audit trail rather than pre-emptively (overblocking kills
|
|
234
|
+
* legitimate AI-industry coverage).
|
|
235
|
+
*/
|
|
236
|
+
const CHINA_POLITICS_RE = /习近平|李克强|李强|党中央|政治局|中南海|政府工作报告|国台办|外交部发言人|两会/;
|
|
237
|
+
function isChinaPolitics(doc: Doc): boolean {
|
|
238
|
+
return CHINA_POLITICS_RE.test(`${doc.title ?? ''}\n${(doc.body ?? '').slice(0, 2000)}`);
|
|
239
|
+
}
|
|
240
|
+
|
|
230
241
|
function passesGates(doc: Doc, planDate: string): boolean {
|
|
242
|
+
if (isChinaPolitics(doc)) return false;
|
|
231
243
|
const meta = asRecord(doc.metadata);
|
|
232
244
|
const tags = Array.isArray(meta.tags) ? meta.tags.filter((t): t is string => typeof t === 'string') : [];
|
|
233
245
|
if (tags.some((tag) => NON_NEWS_TAGS.has(tag.toLowerCase()))) return false;
|
|
@@ -23435,7 +23435,12 @@ header = ${curlQuote(`x-goog-api-key: ${apiKey}`)}
|
|
|
23435
23435
|
"-d",
|
|
23436
23436
|
"@-",
|
|
23437
23437
|
url2
|
|
23438
|
-
], {
|
|
23438
|
+
], {
|
|
23439
|
+
input: JSON.stringify(body),
|
|
23440
|
+
encoding: "utf8",
|
|
23441
|
+
timeout: 310000,
|
|
23442
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
23443
|
+
});
|
|
23439
23444
|
} catch (error51) {
|
|
23440
23445
|
const e = error51;
|
|
23441
23446
|
const details = (typeof e.stderr === "string" ? e.stderr.trim() : "") || e.message || "curl request failed";
|
|
@@ -174,6 +174,9 @@ export const createGoogle: ProviderFactory = (options?: ProviderOptions): ImageP
|
|
|
174
174
|
// the key and the proxy password into logs. `-sS` (not `-s`) keeps curl's own diagnostic on
|
|
175
175
|
// stderr, so `details` no longer falls back to that argv-bearing message.
|
|
176
176
|
//
|
|
177
|
+
// `stdio: ['pipe', 'pipe', 'pipe']` is explicit because Bun's `execFileSync` otherwise
|
|
178
|
+
// tees the child's stderr to the parent's stderr even though it also captures it.
|
|
179
|
+
//
|
|
177
180
|
// File IO goes through the `@gobing-ai/ts-runtime` seam (`no-direct-fs-io`): the seam is
|
|
178
181
|
// text-only with no `mode` argument, and the file lives in `os.tmpdir()`, which is a
|
|
179
182
|
// per-user directory (0700 on macOS).
|
|
@@ -199,7 +202,12 @@ export const createGoogle: ProviderFactory = (options?: ProviderOptions): ImageP
|
|
|
199
202
|
'@-',
|
|
200
203
|
url,
|
|
201
204
|
],
|
|
202
|
-
{
|
|
205
|
+
{
|
|
206
|
+
input: JSON.stringify(body),
|
|
207
|
+
encoding: 'utf8',
|
|
208
|
+
timeout: 310_000,
|
|
209
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
210
|
+
},
|
|
203
211
|
);
|
|
204
212
|
} catch (error: unknown) {
|
|
205
213
|
const e = error as { message?: string; stderr?: string | Buffer };
|
package/plugins/kk/plugin.json
CHANGED
|
@@ -1233,7 +1233,9 @@ function dailyPublishXhs(args: string[]): void {
|
|
|
1233
1233
|
* outcome never enters `publish_status` (surfdash + podcast remain the publication contract).
|
|
1234
1234
|
*/
|
|
1235
1235
|
function dailyPublishWechat(args: string[]): void {
|
|
1236
|
-
const [
|
|
1236
|
+
const [workDirRaw = '', runDate = '', pluginsPath = '', live = 'false', collectionArg = ''] = args;
|
|
1237
|
+
// Metadata carries file paths consumed by the plugin child process (cwd = plugin dir) - never emit relatives.
|
|
1238
|
+
const workDir = resolve(workDirRaw);
|
|
1237
1239
|
const article = join(workDir, '22-article', `${runDate}_06_article_content.json`);
|
|
1238
1240
|
const input = join(workDir, '31-publish', `${runDate}_20_publish-wechat_content.json`);
|
|
1239
1241
|
const result = join(workDir, '31-publish', `${runDate}_20_publish-wechat_result.json`);
|
|
@@ -1250,8 +1252,35 @@ function dailyPublishWechat(args: string[]): void {
|
|
|
1250
1252
|
return;
|
|
1251
1253
|
}
|
|
1252
1254
|
const packaged = requireJson(article, 'publish-wechat');
|
|
1255
|
+
// MP draft enrichment (operator preference 2026-09-29): 原创+作者, 赞赏, 合集, 原文链接
|
|
1256
|
+
// (the surfdash post), AI 生成声明, cover. The brand strings are this daily show's constants.
|
|
1257
|
+
const cover = join(workDir, '23-cover', `${runDate}_07_cover_cover.png`);
|
|
1258
|
+
const audio = join(workDir, '30-audio', `${runDate}_11_generate_content.mp3`);
|
|
1259
|
+
const wechatmp: Record<string, unknown> = {
|
|
1260
|
+
original: true,
|
|
1261
|
+
aiGenerated: true,
|
|
1262
|
+
author: '冰原奔狼',
|
|
1263
|
+
collection: collectionArg || '每日AI资讯',
|
|
1264
|
+
...(isFile(cover) ? { coverPath: cover } : {}),
|
|
1265
|
+
// Audio block at the article top (operator request 2026-09-29): the daily MP3 goes to
|
|
1266
|
+
// the 素材库 under the article title; the plugin reuses an existing same-title material.
|
|
1267
|
+
...(isFile(audio)
|
|
1268
|
+
? {
|
|
1269
|
+
audioPath: audio,
|
|
1270
|
+
audioTitle: typeof packaged.title === 'string' ? packaged.title : '',
|
|
1271
|
+
audioCoverPath: isFile(cover) ? cover : '',
|
|
1272
|
+
}
|
|
1273
|
+
: {}),
|
|
1274
|
+
};
|
|
1275
|
+
// WeChat-only closing line (operator request 2026-09-29): point readers/listeners back to
|
|
1276
|
+
// the show's platforms. Appended to the wechat input body only, never the master article.
|
|
1277
|
+
const outro = '\n\n欲听往期节目,可至小宇宙、Apple Podcast、YouTube 等平台搜索【咸话咸说】。';
|
|
1253
1278
|
sted('write', input, () =>
|
|
1254
|
-
writeJsonText(input, {
|
|
1279
|
+
writeJsonText(input, {
|
|
1280
|
+
...packaged,
|
|
1281
|
+
body: `${typeof packaged.body === 'string' ? packaged.body : ''}${outro}`,
|
|
1282
|
+
metadata: { ...metadataOf(packaged), publishStatus: want, wechatmp },
|
|
1283
|
+
}),
|
|
1255
1284
|
);
|
|
1256
1285
|
// `soft` keeps the WeChat target non-blocking: the plugin still writes its own failed Result
|
|
1257
1286
|
// and prints the failing step (session preflight, editor paste, save control), which is what
|
|
@@ -1575,6 +1604,17 @@ function dailyPublishPrep(args: string[]): void {
|
|
|
1575
1604
|
stampId3Tags(audioFile, typeof article.title === 'string' ? article.title : '', runDate, artist, album);
|
|
1576
1605
|
const resolvedCover = resolve(coverPath);
|
|
1577
1606
|
const hasCover = existsSync(resolvedCover);
|
|
1607
|
+
// Daily-show itunes convention: season = year, episode = day-of-year. run_date arrives
|
|
1608
|
+
// dashed (`$(date +%F)`) or digits-only (formatRunDate); map.ts forwards numbers only,
|
|
1609
|
+
// so a malformed run_date omits both tags instead of failing the publish.
|
|
1610
|
+
const digits = runDate.replaceAll(/\D/g, '');
|
|
1611
|
+
const season = Number(digits.slice(0, 4));
|
|
1612
|
+
const month = Number(digits.slice(4, 6));
|
|
1613
|
+
const day = Number(digits.slice(6, 8));
|
|
1614
|
+
const episode =
|
|
1615
|
+
digits.length === 8 && month >= 1 && month <= 12 && day >= 1 && day <= 31
|
|
1616
|
+
? Math.round((Date.UTC(season, month - 1, day) - Date.UTC(season, 0, 1)) / 86_400_000) + 1
|
|
1617
|
+
: 0;
|
|
1578
1618
|
// podcast-pub's documented contract reads metadata.coverImage (map.ts); options.coverPath
|
|
1579
1619
|
// alone was silently dropped by the mapper — episodes published without a cover.
|
|
1580
1620
|
const merged = {
|
|
@@ -1584,6 +1624,7 @@ function dailyPublishPrep(args: string[]): void {
|
|
|
1584
1624
|
audioFile,
|
|
1585
1625
|
runDate,
|
|
1586
1626
|
...(hasCover ? { coverImage: resolvedCover } : {}),
|
|
1627
|
+
...(episode > 0 ? { itunes: { season, episode } } : {}),
|
|
1587
1628
|
},
|
|
1588
1629
|
options: {
|
|
1589
1630
|
audioFile,
|
|
@@ -1765,7 +1806,7 @@ function dailyPublishPartial(args: string[]): void {
|
|
|
1765
1806
|
* Idempotent per post; commits + pushes the surfdash checkout (SURFDASH_AUTO_PUSH=0 keeps commit-only).
|
|
1766
1807
|
*/
|
|
1767
1808
|
function dailyPatchSurfdash(args: string[]): void {
|
|
1768
|
-
const [workDir = '', runDate = '', surfdashRoot = ''] = args;
|
|
1809
|
+
const [workDir = '', runDate = '', surfdashRoot = '', pluginsPath = ''] = args;
|
|
1769
1810
|
if (!surfdashRoot || !existsSync(surfdashRoot)) {
|
|
1770
1811
|
console.log(`patch-surfdash: surfdash root '${surfdashRoot}' not found — skip`);
|
|
1771
1812
|
return;
|
|
@@ -1866,6 +1907,39 @@ function dailyPatchSurfdash(args: string[]): void {
|
|
|
1866
1907
|
changed++;
|
|
1867
1908
|
}
|
|
1868
1909
|
}
|
|
1910
|
+
// 20260929 prod feedback: CMS cards (homepage hero/tag/search) read EmDash rows whose
|
|
1911
|
+
// featured_image is snapshotted at translate-sync time — i.e. the pre-patch
|
|
1912
|
+
// `./assets/cover.png`, which the deployed worker does not serve (404). The patch only
|
|
1913
|
+
// rewrites files, so re-sync the EmDash rows from the patched files through the same
|
|
1914
|
+
// `kk executor` path as translate-sync (its plugin child gets EMDASH_API_TOKEN from kk
|
|
1915
|
+
// config; a direct orchestrator spawn gets 403 from prod dev-bypass). Runs before the
|
|
1916
|
+
// changed===0 early-return so re-runs heal rows an earlier commit left stale
|
|
1917
|
+
// (20260925/20260929). Fixture tests pass no plugins-path and must not shell out.
|
|
1918
|
+
if (pluginsPath === '') {
|
|
1919
|
+
console.log('patch-surfdash: no plugins-path — skip emdash re-sync');
|
|
1920
|
+
} else {
|
|
1921
|
+
const syncIn = join(workDir, '31-publish', `${runDate}_23_patch-surfdash_sync.json`);
|
|
1922
|
+
const syncResult = join(workDir, '31-publish', `${runDate}_23_patch-surfdash_sync-result.json`);
|
|
1923
|
+
sted('write', syncIn, () =>
|
|
1924
|
+
writeJqText(syncIn, { body: '', options: { operation: 'sync', postPath: join(slugDir, 'index.md') } }),
|
|
1925
|
+
);
|
|
1926
|
+
const status = kk(
|
|
1927
|
+
['executor', 'run', 'surfdash-pub', '--in', syncIn, '--out', syncResult, ...pluginsArgs(pluginsPath)],
|
|
1928
|
+
{ unset: NESTED_SPUR_UNSET, soft: true },
|
|
1929
|
+
);
|
|
1930
|
+
const result = isFile(syncResult) ? readJson(syncResult) : null;
|
|
1931
|
+
const ok =
|
|
1932
|
+
status === 0 &&
|
|
1933
|
+
result !== null &&
|
|
1934
|
+
(result.ok === true || (result.targets as Array<{ ok?: boolean }> | undefined)?.[0]?.ok === true);
|
|
1935
|
+
if (!ok) {
|
|
1936
|
+
console.log(
|
|
1937
|
+
`patch-surfdash: WARNING: emdash re-sync failed (exit ${status}) — CMS cards keep the stale cover; fix: kk executor run surfdash-pub --in ${syncIn} --out ${syncResult} --plugins-path ${pluginsPath}`,
|
|
1938
|
+
);
|
|
1939
|
+
} else {
|
|
1940
|
+
console.log('patch-surfdash: emdash rows re-synced from patched files');
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1869
1943
|
if (changed === 0) {
|
|
1870
1944
|
console.log('patch-surfdash: nothing to change');
|
|
1871
1945
|
return;
|
|
@@ -76,6 +76,8 @@ vars:
|
|
|
76
76
|
# live — 发表 only on an ad-hoc wechat_live="true" run, deliberately, by hand.
|
|
77
77
|
wechat_enabled: "true"
|
|
78
78
|
wechat_live: "false"
|
|
79
|
+
# 合集 tag for the MP draft (passed to wechatmp-pub); other shows/collections override per workflow.
|
|
80
|
+
wechat_collection: "每日AI资讯"
|
|
79
81
|
plan_max_items: "20"
|
|
80
82
|
plan_translate_enabled: "true"
|
|
81
83
|
article_write_enabled: "true"
|
|
@@ -191,7 +193,7 @@ states:
|
|
|
191
193
|
# ADR-115 error tier: keep this prompt <= 1000 chars (0133 R3).
|
|
192
194
|
input: |
|
|
193
195
|
Read ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json — metadata.docs (Doc[]) is the translated plan; don't edit it.
|
|
194
|
-
Write a morning-news prose article in natural simplified Chinese: title line "# 每日 AI 资讯 — ${vars.run_date}", an intro paragraph (2–3 sentences), then one section per item (## numbered title, 2–4 sentences of flowing prose, source link [来源:<label>](<sourceUri>) inline), a closing paragraph tying items together.
|
|
196
|
+
Write a morning-news prose article in natural simplified Chinese: title line "# 每日 AI 资讯 — ${vars.run_date}", an intro paragraph (2–3 sentences), then one section per item (## numbered title, 2–4 sentences of flowing prose, source link [来源:<label>](<sourceUri>) inline), a closing paragraph tying items together. 内容合规(平台审核):全文不出现中国现任或历任党、国家、政府最高领导人的姓名,必须提及时以职务代称(如「国家主席」「国务院总理」);不书写中国国内政治相关议题。
|
|
195
197
|
Narrative register — smooth transitions between items, no list-like bulleting, no scaffold labels (「可做角度」「背景」「潜在影响」), no hedging (「读者宜另行核实」「仍待验证」). Keep facts/URLs from the plan verbatim; product/model names as-is; 「」 quotes.
|
|
196
198
|
Output the markdown to ${vars.work_dir}/22-article/${vars.run_date}_06_article-write_body.md.
|
|
197
199
|
expectFile: ${vars.work_dir}/22-article/${vars.run_date}_06_article-write_body.md
|
|
@@ -255,7 +257,7 @@ states:
|
|
|
255
257
|
input: |
|
|
256
258
|
Read ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json — a JSON array of docs; use each doc's `id`; don't edit it. Also read the style contract ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_style.md and obey its char caps and banned phrases.
|
|
257
259
|
For each doc write a `lead` (a spoken bridge into the title, not containing it) and an `insight` (why it matters, grounded only in that doc's facts).
|
|
258
|
-
Write an `intro` hooking today's actual themes and an `outro`; no greeting or sign-off — the builder adds both.
|
|
260
|
+
Write an `intro` hooking today's actual themes and an `outro`; no greeting or sign-off — the builder adds both. 内容合规:不出现中国现任、历任最高领导人姓名(以职务代称),不涉及中国国内政治。
|
|
259
261
|
Output only the JSON object {"version":1,"intro":"…","outro":"…","items":[{"id":"…","lead":"…","insight":"…"}]} with one item per doc id to ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_plan.json.
|
|
260
262
|
expectFile: ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_plan.json
|
|
261
263
|
allowAppend: false
|
|
@@ -355,7 +357,7 @@ states:
|
|
|
355
357
|
input: |
|
|
356
358
|
Read ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json — a JSON array of news docs; don't edit it.
|
|
357
359
|
Take item 1 only (its "title" and "metadata.brief") — the lead story.
|
|
358
|
-
Write
|
|
360
|
+
Write an XHS note: "title" (≤20 chars) and "body" (Chinese note ≤1000 chars, ending with 3-5 hashtags). 不提领导人姓名,不涉国内政治。
|
|
359
361
|
Write exactly ${vars.xhs_cards} cards: card 1 role "cover", the rest "content", except a final "closing". Each card carries "role", "copy" (Chinese on-card headline ≤12 chars), "points" (2-4 short Chinese bullets), "layout", "subject" (one concrete English visual scene), "sourcePoint" (the lead-story fact that card uses).
|
|
360
362
|
Ground every "copy", "points" and "sourcePoint" in that story alone — no invented facts. No text, letters, numbers, logos or real people in "subject".
|
|
361
363
|
Output strictly valid JSON {"title": "...", "body": "...", "cards": [...]} to ${vars.work_dir}/25-xhs/${vars.run_date}_18_xhs_plan.json.
|
|
@@ -383,7 +385,7 @@ states:
|
|
|
383
385
|
onError: continue
|
|
384
386
|
options:
|
|
385
387
|
command: >-
|
|
386
|
-
kk stage daily-publish-wechat "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.wechat_live}"
|
|
388
|
+
kk stage daily-publish-wechat "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.wechat_live}" "${vars.wechat_collection}"
|
|
387
389
|
|
|
388
390
|
- id: publish
|
|
389
391
|
description: "Classify the sequenced surfdash → podcast outcomes full|partial|none into publish-status.txt (exit 0 in all three — routing is transition-owned; task 0118: per-target classification over the merged result.json)"
|
|
@@ -398,12 +400,12 @@ states:
|
|
|
398
400
|
var: publish_status
|
|
399
401
|
|
|
400
402
|
- id: patch-surfdash
|
|
401
|
-
description: "20260917 #4/#6 — patch published surfdash posts: podcast cover URL into image/og_image frontmatter + podcast cross-link (zh/en/ja), commit+push"
|
|
403
|
+
description: "20260917 #4/#6 — patch published surfdash posts: podcast cover URL into image/og_image frontmatter + podcast cross-link (zh/en/ja), re-sync EmDash rows so CMS cards get the CDN cover (20260929), commit+push"
|
|
402
404
|
onEnter:
|
|
403
405
|
- kind: shell
|
|
404
406
|
options:
|
|
405
407
|
command: >-
|
|
406
|
-
kk stage daily-patch-surfdash "${vars.work_dir}" "${vars.run_date}" "${vars.surfdash_root}"
|
|
408
|
+
kk stage daily-patch-surfdash "${vars.work_dir}" "${vars.run_date}" "${vars.surfdash_root}" "${vars.plugins_path}"
|
|
407
409
|
|
|
408
410
|
- id: publish-partial
|
|
409
411
|
description: "Partial publish — at least one target published, at least one failed; echo warning, write a .spur/run marker, preserve result.json for --retry-from, continue to done (task 0118: surfdash failure = no-URL fallback notes; podcast failure = its own partial)"
|
|
@@ -604,6 +606,7 @@ transitions:
|
|
|
604
606
|
- from: review
|
|
605
607
|
to: failed
|
|
606
608
|
description: "VoiceScript rejected -> abort"
|
|
609
|
+
terminalReason: failed-agent
|
|
607
610
|
guard:
|
|
608
611
|
kind: shell
|
|
609
612
|
options:
|
|
@@ -684,9 +687,17 @@ transitions:
|
|
|
684
687
|
guard:
|
|
685
688
|
kind: always
|
|
686
689
|
|
|
690
|
+
- from: xhs-publish
|
|
691
|
+
to: wechat-publish
|
|
692
|
+
description: "XHS dispatched (or skipped: onError continue) -> chain the WeChat draft when wechat_enabled. 20260929 dogfood: publish-podcast's edges are first-match-wins, so the direct publish-podcast -> wechat-publish edge is unreachable whenever xhs_enabled=true — wechat must hang off the xhs path"
|
|
693
|
+
guard:
|
|
694
|
+
kind: shell
|
|
695
|
+
options:
|
|
696
|
+
command: 'test "${vars.wechat_enabled}" = "true"'
|
|
697
|
+
|
|
687
698
|
- from: xhs-publish
|
|
688
699
|
to: publish
|
|
689
|
-
description: "XHS dispatched (or skipped: onError continue) -> classify the surfdash/podcast outcomes; XHS contributes no target to the classification"
|
|
700
|
+
description: "XHS dispatched (or skipped: onError continue) -> classify the surfdash/podcast outcomes; XHS contributes no target to the classification (direct edge when wechat_enabled is false)"
|
|
690
701
|
guard:
|
|
691
702
|
kind: always
|
|
692
703
|
|
|
@@ -735,6 +746,7 @@ transitions:
|
|
|
735
746
|
- from: publish
|
|
736
747
|
to: failed
|
|
737
748
|
description: "Publish none (all targets failed or fan-out crash) -> pipeline failed"
|
|
749
|
+
terminalReason: failed-check
|
|
738
750
|
guard:
|
|
739
751
|
kind: shell
|
|
740
752
|
options:
|