@gobing-ai/knowledge-kit 0.1.9 → 0.1.11

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.
Files changed (72) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/dist/index.js +132 -25
  3. package/package.json +1 -1
  4. package/plugins/generations/content-gen/dist/index.js +4 -0
  5. package/plugins/generations/core-facts-gen/dist/index.js +4 -0
  6. package/plugins/generations/daily-article-gen/dist/index.js +4 -0
  7. package/plugins/generations/dailynews-gen/dist/index.js +4 -0
  8. package/plugins/generations/episode-plan-gen/dist/index.js +102 -1
  9. package/plugins/generations/episode-plan-gen/src/index.ts +2 -4
  10. package/plugins/generations/gemini-tts/dist/index.js +4 -0
  11. package/plugins/generations/image-gen/dist/index.js +4 -0
  12. package/plugins/generations/news-report-gen/dist/index.js +4 -0
  13. package/plugins/generations/voice-gen/dist/index.js +4 -0
  14. package/plugins/generations/voxcpm-tts/dist/index.js +4 -0
  15. package/plugins/ingestions/aihot-ingest/dist/index.js +4 -0
  16. package/plugins/ingestions/horizon-ingest/dist/index.js +4 -0
  17. package/plugins/ingestions/last30days-ingest/dist/index.js +11 -3
  18. package/plugins/ingestions/last30days-ingest/src/index.ts +14 -3
  19. package/plugins/ingestions/web-search/dist/index.js +4 -0
  20. package/plugins/kk/plugin.json +1 -1
  21. package/plugins/kk/scripts/daily-scheduled-run.ts +298 -0
  22. package/plugins/kk/scripts/daily-stages.ts +1083 -49
  23. package/plugins/kk/scripts/kk-workflow-stages.ts +76 -10
  24. package/plugins/kk/scripts/safety-artifact-check.ts +235 -0
  25. package/plugins/kk/scripts/sentences.ts +134 -0
  26. package/plugins/kk/scripts/series-names.ts +124 -0
  27. package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +194 -113
  28. package/plugins/kk/workflows/kk-daily-safety.yaml +160 -36
  29. package/plugins/publishings/bilibili-pub/dist/index.js +6 -1
  30. package/plugins/publishings/bilibili-pub/dist/profile-check.js +2 -1
  31. package/plugins/publishings/cnblogs-pub/dist/index.js +6 -1
  32. package/plugins/publishings/cnblogs-pub/dist/profile-check.js +2 -1
  33. package/plugins/publishings/csdn-pub/dist/index.js +6 -1
  34. package/plugins/publishings/csdn-pub/dist/profile-check.js +6 -1
  35. package/plugins/publishings/emdash-pub/dist/index.js +4 -0
  36. package/plugins/publishings/infoq-pub/dist/index.js +6 -1
  37. package/plugins/publishings/infoq-pub/dist/profile-check.js +6 -1
  38. package/plugins/publishings/jike-pub/dist/index.js +6 -1
  39. package/plugins/publishings/jike-pub/dist/profile-check.js +6 -1
  40. package/plugins/publishings/juejin-pub/dist/index.js +6 -1
  41. package/plugins/publishings/juejin-pub/dist/profile-check.js +6 -1
  42. package/plugins/publishings/medium-pub/dist/index.js +4 -0
  43. package/plugins/publishings/podcast-pub/dist/index.js +151 -16
  44. package/plugins/publishings/podcast-pub/src/index.ts +62 -5
  45. package/plugins/publishings/podcast-pub/src/seo.ts +2 -4
  46. package/plugins/publishings/podcast-pub/src/show-notes.ts +3 -8
  47. package/plugins/publishings/qiita-pub/dist/index.js +4 -0
  48. package/plugins/publishings/shipinhao-pub/dist/index.js +6 -1
  49. package/plugins/publishings/shipinhao-pub/dist/profile-check.js +2 -1
  50. package/plugins/publishings/sspai-pub/dist/index.js +6 -1
  51. package/plugins/publishings/sspai-pub/dist/profile-check.js +2 -1
  52. package/plugins/publishings/substack-pub/dist/index.js +6 -1
  53. package/plugins/publishings/substack-pub/dist/profile-check.js +6 -1
  54. package/plugins/publishings/surfdash-pub/dist/index.js +691 -63
  55. package/plugins/publishings/surfdash-pub/src/digest-check.ts +284 -0
  56. package/plugins/publishings/surfdash-pub/src/index.ts +656 -84
  57. package/plugins/publishings/surfdash-pub/src/patch.ts +44 -7
  58. package/plugins/publishings/threads-pub/dist/index.js +4 -0
  59. package/plugins/publishings/wechatmp-pub/dist/index.js +937 -98
  60. package/plugins/publishings/wechatmp-pub/dist/profile-check.js +64 -3
  61. package/plugins/publishings/wechatmp-pub/src/flow.ts +1125 -85
  62. package/plugins/publishings/wechatmp-pub/src/index.ts +112 -30
  63. package/plugins/publishings/wechatmp-pub/src/page.ts +105 -0
  64. package/plugins/publishings/weibo-pub/dist/index.js +6 -1
  65. package/plugins/publishings/weibo-pub/dist/profile-check.js +6 -1
  66. package/plugins/publishings/x-pub/dist/index.js +6 -1
  67. package/plugins/publishings/x-pub/dist/profile-check.js +6 -1
  68. package/plugins/publishings/xhs-pub/dist/index.js +6 -1
  69. package/plugins/publishings/xhs-pub/dist/profile-check.js +6 -1
  70. package/plugins/publishings/xiaoyuzhou-pub/dist/index.js +6 -1
  71. package/plugins/publishings/xiaoyuzhou-pub/dist/profile-check.js +2 -1
  72. package/plugins/publishings/zenn-pub/dist/index.js +4 -0
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "plugins": [
8
8
  {
9
- "version": "0.1.9",
9
+ "version": "0.1.11",
10
10
  "name": "kk",
11
11
  "source": "./plugins/kk",
12
12
  "description": "knowledge-kit — a set of toolkit for knowledge management"
package/dist/index.js CHANGED
@@ -37862,9 +37862,11 @@ var init_dist6 = __esm(() => {
37862
37862
  // ../../packages/publish-harness/src/bot-fs.ts
37863
37863
  import {
37864
37864
  chmodSync,
37865
+ closeSync,
37865
37866
  copyFileSync,
37866
37867
  lstatSync,
37867
37868
  mkdirSync as mkdirSync4,
37869
+ openSync,
37868
37870
  readdirSync as readdirSync4,
37869
37871
  realpathSync as realpathSync5,
37870
37872
  rmdirSync
@@ -37906,6 +37908,9 @@ function rmdir(path) {
37906
37908
  function chmod(path, mode) {
37907
37909
  chmodSync(path, mode);
37908
37910
  }
37911
+ function createExclusiveFile(path) {
37912
+ closeSync(openSync(path, "wx", 384));
37913
+ }
37909
37914
  function copyProfileEntry(src, dest, options = {}) {
37910
37915
  const stat = lstat(src);
37911
37916
  if (stat === null)
@@ -38790,6 +38795,8 @@ __export(exports_browser, {
38790
38795
  canonicalize: () => canonicalize,
38791
38796
  captureSessionFacts: () => captureSessionFacts,
38792
38797
  checkChannelSession: () => checkChannelSession,
38798
+ chmod: () => chmod,
38799
+ createExclusiveFile: () => createExclusiveFile,
38793
38800
  createKkBrowserLauncher: () => createKkBrowserLauncher,
38794
38801
  createShadowFinder: () => createShadowFinder,
38795
38802
  enterDelegatedLease: () => enterDelegatedLease,
@@ -38807,6 +38814,7 @@ __export(exports_browser, {
38807
38814
  loginBot: () => loginBot,
38808
38815
  logoutBot: () => logoutBot,
38809
38816
  markdownToHtml: () => markdownToHtml,
38817
+ mkdirExclusive: () => mkdirExclusive,
38810
38818
  normalizeBotDefaultUrl: () => normalizeBotDefaultUrl,
38811
38819
  normalizeSessionExpiry: () => normalizeSessionExpiry,
38812
38820
  normalizeSessionIdentity: () => normalizeSessionIdentity,
@@ -38821,6 +38829,7 @@ __export(exports_browser, {
38821
38829
  resolveBotProfileDir: () => resolveBotProfileDir,
38822
38830
  resolveChannelProfileDir: () => resolveChannelProfileDir,
38823
38831
  resolveSelector: () => resolveSelector,
38832
+ rmdir: () => rmdir,
38824
38833
  runProfileCli: () => runProfileCli,
38825
38834
  uploadFile: () => uploadFile,
38826
38835
  verifyLeaseHeld: () => verifyLeaseHeld,
@@ -39625,6 +39634,7 @@ var init_browser = __esm(() => {
39625
39634
  init_dist();
39626
39635
  init_bot();
39627
39636
  init_bot_fs();
39637
+ init_bot_fs();
39628
39638
  init_bot_transaction();
39629
39639
  init_src();
39630
39640
  init_profile_dir2();
@@ -42385,6 +42395,10 @@ function getByName(result, name) {
42385
42395
  }
42386
42396
  // ../../packages/kk-core/src/render-md.ts
42387
42397
  if (false) {}
42398
+ // ../../packages/kk-core/src/sentences.ts
42399
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
42400
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
42401
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
42388
42402
  // src/invoke.ts
42389
42403
  init_dist();
42390
42404
 
@@ -48178,11 +48192,47 @@ async function prepareAction(options) {
48178
48192
  failLoud2("kk.daily.prepare.failed", error);
48179
48193
  }
48180
48194
  }
48181
- function targetsOf(path) {
48182
- if (!existsSync5(path))
48183
- return [];
48184
- const parsed = parseJson3(readFileSync7(path, "utf-8"));
48185
- return Array.isArray(parsed?.targets) ? parsed.targets.filter((e) => e !== null && typeof e === "object") : [];
48195
+ var PRIMARY_PUBLISH_TARGETS = ["surfdash-pub", "podcast-pub"];
48196
+ function isRecord2(value) {
48197
+ return value !== null && typeof value === "object" && !Array.isArray(value);
48198
+ }
48199
+ function failedPrimaryTarget(target, reason) {
48200
+ return { target, ok: false, status: "failed", error: reason };
48201
+ }
48202
+ function readPrimaryReceipt(path, expectedTarget) {
48203
+ if (!existsSync5(path)) {
48204
+ const issue = `${expectedTarget} receipt file is missing`;
48205
+ return { targets: [failedPrimaryTarget(expectedTarget, issue)], issue };
48206
+ }
48207
+ let parsed;
48208
+ try {
48209
+ parsed = parseJson3(readFileSync7(path, "utf-8"));
48210
+ } catch {
48211
+ const issue = `${expectedTarget} receipt is not valid JSON`;
48212
+ return { targets: [failedPrimaryTarget(expectedTarget, issue)], issue };
48213
+ }
48214
+ if (!isRecord2(parsed) || !Array.isArray(parsed.targets)) {
48215
+ const issue = `${expectedTarget} receipt has no targets array`;
48216
+ return { targets: [failedPrimaryTarget(expectedTarget, issue)], issue };
48217
+ }
48218
+ const rawTargets = parsed.targets;
48219
+ const targets = rawTargets.filter(isRecord2);
48220
+ const issues = [];
48221
+ if (targets.length !== rawTargets.length) {
48222
+ issues.push(`${expectedTarget} receipt contains malformed target records`);
48223
+ }
48224
+ if (targets.length === 0) {
48225
+ issues.push(`${expectedTarget} receipt targets array is empty`);
48226
+ }
48227
+ const observedExpected = targets.some((entry) => entry.target === expectedTarget);
48228
+ if (!observedExpected) {
48229
+ targets.push(failedPrimaryTarget(expectedTarget, `${expectedTarget} receipt has no named target record`));
48230
+ issues.push(`${expectedTarget} receipt has no named target record`);
48231
+ }
48232
+ return { targets, issue: issues.length === 0 ? undefined : issues.join("; ") };
48233
+ }
48234
+ function isPublishedPrimaryTarget(entry) {
48235
+ return PRIMARY_PUBLISH_TARGETS.some((target) => target === entry.target) && entry.ok === true && entry.status === "published";
48186
48236
  }
48187
48237
  async function publishClassifyAction(options) {
48188
48238
  try {
@@ -48192,14 +48242,36 @@ async function publishClassifyAction(options) {
48192
48242
  const statusPath = join7(dir, `${runDate}_17_publish_publish-status.txt`);
48193
48243
  const surfdash = join7(dir, `${runDate}_14_publish-surfdash_result.json`);
48194
48244
  const podcast = join7(dir, `${runDate}_16_publish-podcast_result.json`);
48195
- const bothWritten = existsSync5(surfdash) && existsSync5(podcast);
48196
- const targets = bothWritten ? [...targetsOf(surfdash), ...targetsOf(podcast)] : [];
48197
- const okCount = targets.filter((entry) => entry.ok === true && entry.status === "published").length;
48245
+ const receipts = [readPrimaryReceipt(surfdash, "surfdash-pub"), readPrimaryReceipt(podcast, "podcast-pub")];
48246
+ const targets = receipts.flatMap((receipt) => receipt.targets);
48247
+ const receiptIssues = receipts.flatMap((receipt) => receipt.issue === undefined ? [] : [receipt.issue]);
48248
+ const okCount = targets.filter(isPublishedPrimaryTarget).length;
48198
48249
  const total = targets.length;
48199
- const status = okCount > 0 && okCount === total ? "full" : okCount > 0 ? "partial" : "none";
48250
+ const exactPrimarySet = receiptIssues.length === 0 && targets.length === PRIMARY_PUBLISH_TARGETS.length && PRIMARY_PUBLISH_TARGETS.every((target) => targets.filter((entry) => entry.target === target).length === 1) && targets.every(isPublishedPrimaryTarget);
48251
+ let status = exactPrimarySet ? "full" : okCount > 0 ? "partial" : "none";
48200
48252
  mkdirSync3(dir, { recursive: true });
48201
- writeFileSync4(resultPath, `${JSON.stringify({ ok: okCount === total && total > 0, targets }, null, 2)}`);
48202
- writeFileSync4(statusPath, status);
48253
+ const writeClassification = (completenessError) => {
48254
+ const result = {
48255
+ ok: status === "full",
48256
+ targets,
48257
+ ...receiptIssues.length === 0 ? {} : { issues: receiptIssues },
48258
+ ...completenessError === undefined ? {} : { completenessError }
48259
+ };
48260
+ writeFileSync4(resultPath, `${JSON.stringify(result, null, 2)}`);
48261
+ writeFileSync4(statusPath, status);
48262
+ };
48263
+ writeClassification();
48264
+ const writeSummary = (missing, completenessError) => {
48265
+ writeFileSync4(join7(workDir, "publish-classify.summary.txt"), `${JSON.stringify({
48266
+ status,
48267
+ published: okCount,
48268
+ targets: total,
48269
+ ...receiptIssues.length === 0 ? {} : { issues: receiptIssues },
48270
+ ...completenessError === undefined ? {} : { completenessError },
48271
+ missing
48272
+ })}
48273
+ `);
48274
+ };
48203
48275
  writeFileSync4(join7(workDir, "publish-classify.summary.txt"), `${JSON.stringify({ status, published: okCount, targets: total })}
48204
48276
  `);
48205
48277
  if (options.foldTail === true) {
@@ -48215,20 +48287,45 @@ async function publishClassifyAction(options) {
48215
48287
  failLoud2("kk.daily.publish-classify.patch-failed", new Error(`exit ${code}`));
48216
48288
  }
48217
48289
  }
48218
- runStage(["daily-publish-completeness", workDir, runDate]);
48290
+ let completenessError;
48291
+ let completenessCode = 1;
48292
+ try {
48293
+ completenessCode = runStage(["daily-publish-completeness", workDir, runDate]);
48294
+ } catch (error) {
48295
+ completenessError = `completeness stage failed: ${error instanceof Error ? error.message : String(error)}`;
48296
+ }
48219
48297
  const completenessPath = join7(dir, `${runDate}_17_publish_completeness.json`);
48220
48298
  let missing = [];
48221
- if (existsSync5(completenessPath)) {
48299
+ if (completenessError === undefined && completenessCode !== 0) {
48300
+ completenessError = `completeness stage exited ${completenessCode}`;
48301
+ } else if (completenessError === undefined && !existsSync5(completenessPath)) {
48302
+ completenessError = "completeness result artifact is missing";
48303
+ } else if (completenessError === undefined) {
48222
48304
  try {
48223
48305
  const parsed = parseJson3(readFileSync7(completenessPath, "utf-8"));
48224
- if (Array.isArray(parsed?.missing)) {
48225
- missing = parsed.missing.filter((item) => typeof item === "string");
48306
+ if (!isRecord2(parsed) || !Array.isArray(parsed.missing) || !parsed.missing.every((item) => typeof item === "string")) {
48307
+ completenessError = "completeness result has no valid missing[] probe";
48308
+ } else {
48309
+ missing = parsed.missing;
48226
48310
  }
48227
48311
  } catch {
48228
- missing = [];
48312
+ completenessError = "completeness result is not valid JSON";
48229
48313
  }
48230
48314
  }
48231
- consoleOutput.write(JSON.stringify({ status, published: okCount, targets: total, missing }));
48315
+ if (completenessError !== undefined)
48316
+ missing = ["completeness"];
48317
+ if (completenessError !== undefined && status === "full")
48318
+ status = "partial";
48319
+ writeClassification(completenessError);
48320
+ writeSummary(missing, completenessError);
48321
+ consoleOutput.write(JSON.stringify({
48322
+ status,
48323
+ published: okCount,
48324
+ targets: total,
48325
+ missing,
48326
+ ...receiptIssues.length === 0 ? {} : { issues: receiptIssues },
48327
+ ...completenessError === undefined ? {} : { completenessError }
48328
+ }));
48232
48329
  } catch (error) {
48233
48330
  if (error instanceof CommanderError)
48234
48331
  throw error;
@@ -52757,7 +52854,8 @@ async function readSucceeded(fs, receiptsDir, id) {
52757
52854
  }
52758
52855
  }
52759
52856
  async function readLinkMap(fs, receiptsDir, rows) {
52760
- const map = {};
52857
+ const real = {};
52858
+ const all = {};
52761
52859
  for (const source of LINK_SOURCES) {
52762
52860
  const row = rows.find((candidate) => candidate.plugin === source.plugin && candidate.tier === 0);
52763
52861
  if (row === undefined)
@@ -52767,11 +52865,14 @@ async function readLinkMap(fs, receiptsDir, rows) {
52767
52865
  continue;
52768
52866
  try {
52769
52867
  const receipt = JSON.parse(await fs.readFile(path));
52770
- if (receipt.status === "succeeded" && receipt.url)
52771
- map[source.receipt] = receipt.url;
52868
+ if (receipt.status !== "succeeded" || !receipt.url)
52869
+ continue;
52870
+ all[source.receipt] = receipt.url;
52871
+ if (receipt.dummy !== true)
52872
+ real[source.receipt] = receipt.url;
52772
52873
  } catch {}
52773
52874
  }
52774
- return map;
52875
+ return { real, all };
52775
52876
  }
52776
52877
  async function sendPublishMatrix(args, deps = {}) {
52777
52878
  const fs = deps.fs ?? createNodeFileSystem();
@@ -52822,7 +52923,7 @@ async function sendPublishMatrix(args, deps = {}) {
52822
52923
  }
52823
52924
  continue;
52824
52925
  }
52825
- const links = tier === 0 ? {} : await readLinkMap(fs, receiptsDir, plan.rows);
52926
+ const linkMaps = tier === 0 ? { real: {}, all: {} } : await readLinkMap(fs, receiptsDir, plan.rows);
52826
52927
  const sendRowsInput = rows.map((row) => ({
52827
52928
  key: row.id,
52828
52929
  plugin: row.plugin,
@@ -52847,12 +52948,13 @@ async function sendPublishMatrix(args, deps = {}) {
52847
52948
  if (tier === 0)
52848
52949
  return { payloadPath: row.payloadPath };
52849
52950
  const raw = await fs.exists(row.payloadPath) ? await fs.readFile(row.payloadPath) : "";
52951
+ const links = row.mode === "dummy" ? linkMaps.all : linkMaps.real;
52850
52952
  let resolved = raw;
52851
52953
  for (const source of LINK_SOURCES) {
52852
52954
  const url = links[source.receipt];
52853
52955
  if (url === undefined)
52854
52956
  continue;
52855
- resolved = resolved.split(source.placeholder).join(url);
52957
+ resolved = resolved.split(source.placeholder).join(JSON.stringify(url).slice(1, -1));
52856
52958
  }
52857
52959
  if (LINK_SOURCES.some((source) => resolved.includes(source.placeholder))) {
52858
52960
  return { error: "unresolved-link" };
@@ -52984,7 +53086,12 @@ async function runPublishMatrixRssGate(args, deps = {}) {
52984
53086
  };
52985
53087
  let xml;
52986
53088
  try {
52987
- const read = deps.readFeed ?? (async (url) => (await fetch(url)).text());
53089
+ const read = deps.readFeed ?? (async (url) => {
53090
+ const res = await fetch(url, { signal: AbortSignal.timeout(30000) });
53091
+ if (!res.ok)
53092
+ throw new Error(`HTTP ${res.status}`);
53093
+ return res.text();
53094
+ });
52988
53095
  xml = await read(feedUrl);
52989
53096
  } catch (err) {
52990
53097
  return write({
@@ -53123,7 +53230,7 @@ async function runPublishMatrixBundle(args, deps = {}) {
53123
53230
  }
53124
53231
  const hasOwnedVariant = Object.keys(variants).some((key) => key === "article" || key === "episode");
53125
53232
  if (!hasOwnedVariant) {
53126
- echoError("publish-matrix-bundle: no article/episode variant \u2014 the publication contract needs both");
53233
+ echoError("publish-matrix-bundle: no article/episode variant \u2014 the publication contract needs at least one");
53127
53234
  return 1;
53128
53235
  }
53129
53236
  const articleTitle = await readVariantTitle(fs, workDir, variants);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/knowledge-kit",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "An ingest → create → publish content pipeline CLI (Bun).",
5
5
  "repository": {
6
6
  "type": "git",
@@ -12253,6 +12253,10 @@ init_config();
12253
12253
 
12254
12254
  // ../../packages/kk-core/src/render-md.ts
12255
12255
  if (false) {}
12256
+ // ../../packages/kk-core/src/sentences.ts
12257
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
12258
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
12259
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
12256
12260
  // ../../plugins/generations/content-gen/src/index.ts
12257
12261
  init_dist();
12258
12262
 
@@ -12319,6 +12319,10 @@ function uniqueToken() {
12319
12319
  }
12320
12320
  // ../../packages/kk-core/src/render-md.ts
12321
12321
  if (false) {}
12322
+ // ../../packages/kk-core/src/sentences.ts
12323
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
12324
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
12325
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
12322
12326
  // ../../plugins/generations/core-facts-gen/src/index.ts
12323
12327
  init_dist();
12324
12328
  function sourceFileOf(entry) {
@@ -12319,6 +12319,10 @@ function uniqueToken() {
12319
12319
  }
12320
12320
  // ../../packages/kk-core/src/render-md.ts
12321
12321
  if (false) {}
12322
+ // ../../packages/kk-core/src/sentences.ts
12323
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
12324
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
12325
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
12322
12326
  // ../../plugins/generations/daily-article-gen/src/index.ts
12323
12327
  init_dist();
12324
12328
  function sourceLabelOf(doc) {
@@ -12254,6 +12254,10 @@ init_config();
12254
12254
 
12255
12255
  // ../../packages/kk-core/src/render-md.ts
12256
12256
  if (false) {}
12257
+ // ../../packages/kk-core/src/sentences.ts
12258
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
12259
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
12260
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
12257
12261
  // ../../plugins/generations/dailynews-gen/src/index.ts
12258
12262
  init_dist();
12259
12263
 
@@ -12319,6 +12319,107 @@ function uniqueToken() {
12319
12319
  }
12320
12320
  // ../../packages/kk-core/src/render-md.ts
12321
12321
  if (false) {}
12322
+ // ../../packages/kk-core/src/sentences.ts
12323
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
12324
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
12325
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
12326
+ var OPEN_PAIRS = {
12327
+ "\u300C": "\u300D",
12328
+ "\u300E": "\u300F",
12329
+ "\u201C": "\u201D",
12330
+ "\uFF08": "\uFF09",
12331
+ "(": ")"
12332
+ };
12333
+ var CLOSE_PAIRS = {
12334
+ "\u300D": "\u300C",
12335
+ "\u300F": "\u300E",
12336
+ "\u201D": "\u201C",
12337
+ "\uFF09": "\uFF08",
12338
+ ")": "("
12339
+ };
12340
+ function splitSentences(text, options = {}) {
12341
+ if (!text || typeof text !== "string")
12342
+ return [];
12343
+ const results = [];
12344
+ let sentenceStart = 0;
12345
+ let stack = [];
12346
+ let i = 0;
12347
+ const isTerminator = (c) => CJK_TERMINATORS.has(c) || options.cjkOnly !== true && LATIN_TERMINATORS.has(c);
12348
+ while (i < text.length) {
12349
+ const ch = text[i];
12350
+ if (isTerminator(ch)) {
12351
+ let termEnd = i;
12352
+ let hasCjkTerm = false;
12353
+ let hasLatinTerm = false;
12354
+ while (termEnd < text.length) {
12355
+ const termChar = text[termEnd];
12356
+ if (!termChar || !isTerminator(termChar)) {
12357
+ break;
12358
+ }
12359
+ if (CJK_TERMINATORS.has(termChar))
12360
+ hasCjkTerm = true;
12361
+ if (LATIN_TERMINATORS.has(termChar))
12362
+ hasLatinTerm = true;
12363
+ termEnd++;
12364
+ }
12365
+ let closerEnd = termEnd;
12366
+ const tempStack = [...stack];
12367
+ while (closerEnd < text.length) {
12368
+ const closer = text[closerEnd];
12369
+ if (!closer || !ATTACHED_CLOSERS.has(closer)) {
12370
+ break;
12371
+ }
12372
+ const top = tempStack[tempStack.length - 1];
12373
+ if (top !== undefined && top === closer) {
12374
+ tempStack.pop();
12375
+ } else if (CLOSE_PAIRS[closer]) {
12376
+ const idx = tempStack.lastIndexOf(closer);
12377
+ if (idx >= 0)
12378
+ tempStack.splice(idx, 1);
12379
+ }
12380
+ closerEnd++;
12381
+ }
12382
+ let isBoundary = false;
12383
+ if (tempStack.length === 0) {
12384
+ if (hasCjkTerm) {
12385
+ isBoundary = true;
12386
+ } else if (hasLatinTerm) {
12387
+ const nextChar = text[closerEnd];
12388
+ if (closerEnd === text.length || nextChar !== undefined && /\s/.test(nextChar)) {
12389
+ isBoundary = true;
12390
+ }
12391
+ }
12392
+ }
12393
+ if (isBoundary) {
12394
+ const sentence = text.slice(sentenceStart, closerEnd).trim();
12395
+ if (sentence.length > 0) {
12396
+ results.push(sentence);
12397
+ }
12398
+ sentenceStart = closerEnd;
12399
+ stack = tempStack;
12400
+ i = closerEnd;
12401
+ continue;
12402
+ }
12403
+ }
12404
+ const openMatch = OPEN_PAIRS[ch];
12405
+ const closeMatch = CLOSE_PAIRS[ch];
12406
+ if (openMatch) {
12407
+ stack.push(openMatch);
12408
+ } else if (closeMatch) {
12409
+ const idx = stack.lastIndexOf(ch);
12410
+ if (idx >= 0)
12411
+ stack.splice(idx, 1);
12412
+ }
12413
+ i++;
12414
+ }
12415
+ if (sentenceStart < text.length) {
12416
+ const tail = text.slice(sentenceStart).trim();
12417
+ if (tail.length > 0) {
12418
+ results.push(tail);
12419
+ }
12420
+ }
12421
+ return results;
12422
+ }
12322
12423
  // ../../plugins/generations/episode-plan-gen/src/index.ts
12323
12424
  init_dist();
12324
12425
  var SOURCE_BASE = {
@@ -12731,7 +12832,7 @@ function planEpisode(docs, options) {
12731
12832
  return selected.map((r) => {
12732
12833
  const meta = asRecord(r.doc.metadata);
12733
12834
  const ascii = asciiRatio(r.doc.title ?? "");
12734
- const brief = (r.doc.body ?? "").trim().split(/(?<=[\u3002\uFF01\uFF1F])\s*/).slice(0, 2).join("");
12835
+ const brief = splitSentences((r.doc.body ?? "").trim(), { cjkOnly: true }).slice(0, 2).join("");
12735
12836
  const published = docTimestamp(r.doc);
12736
12837
  return {
12737
12838
  ...r.doc,
@@ -1,6 +1,6 @@
1
1
  import { dirname } from 'node:path';
2
2
  import { parseArgs } from 'node:util';
3
- import { type Content, ContentSchema, type Doc, DocListSchema } from '@gobing-ai/kk-core';
3
+ import { type Content, ContentSchema, type Doc, DocListSchema, splitSentences } from '@gobing-ai/kk-core';
4
4
  import { atomicWriteJson, createNodeFileSystem, readJsonFile } from '@gobing-ai/ts-runtime';
5
5
  import { echoError } from '@gobing-ai/ts-utils';
6
6
 
@@ -597,9 +597,7 @@ export function planEpisode(docs: Doc[], options: PlanOptions): Doc[] {
597
597
  // Task 0118: 1–2-sentence briefing (first two 。!?-terminated sentences of the
598
598
  // cleaned body) + best-effort publish timestamp ride the plan metadata so the
599
599
  // podcast show-notes brief/timeline render stays deterministic, no LLM.
600
- const brief = (r.doc.body ?? '')
601
- .trim()
602
- .split(/(?<=[。!?])\s*/)
600
+ const brief = splitSentences((r.doc.body ?? '').trim(), { cjkOnly: true })
603
601
  .slice(0, 2)
604
602
  .join('');
605
603
  const published = docTimestamp(r.doc);
@@ -12257,6 +12257,10 @@ init_config();
12257
12257
 
12258
12258
  // ../../packages/kk-core/src/render-md.ts
12259
12259
  if (false) {}
12260
+ // ../../packages/kk-core/src/sentences.ts
12261
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
12262
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
12263
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
12260
12264
  // ../../plugins/generations/voice-gen/src/index.ts
12261
12265
  init_dist();
12262
12266
 
@@ -12595,6 +12595,10 @@ init_config();
12595
12595
 
12596
12596
  // ../../packages/kk-core/src/render-md.ts
12597
12597
  if (false) {}
12598
+ // ../../packages/kk-core/src/sentences.ts
12599
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
12600
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
12601
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
12598
12602
  // ../../plugins/generations/image-gen/src/index.ts
12599
12603
  init_dist();
12600
12604
 
@@ -12319,6 +12319,10 @@ function uniqueToken() {
12319
12319
  }
12320
12320
  // ../../packages/kk-core/src/render-md.ts
12321
12321
  if (false) {}
12322
+ // ../../packages/kk-core/src/sentences.ts
12323
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
12324
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
12325
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
12322
12326
  // ../../plugins/generations/news-report-gen/src/index.ts
12323
12327
  init_dist();
12324
12328
  var QC_DIMENSIONS = ["quality", "importance", "urgency", "impact"];
@@ -12254,6 +12254,10 @@ init_config();
12254
12254
 
12255
12255
  // ../../packages/kk-core/src/render-md.ts
12256
12256
  if (false) {}
12257
+ // ../../packages/kk-core/src/sentences.ts
12258
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
12259
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
12260
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
12257
12261
  // ../../plugins/generations/voice-gen/src/index.ts
12258
12262
  init_dist();
12259
12263
 
@@ -12257,6 +12257,10 @@ init_config();
12257
12257
 
12258
12258
  // ../../packages/kk-core/src/render-md.ts
12259
12259
  if (false) {}
12260
+ // ../../packages/kk-core/src/sentences.ts
12261
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
12262
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
12263
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
12260
12264
  // ../../plugins/generations/voice-gen/src/index.ts
12261
12265
  init_dist();
12262
12266
 
@@ -12571,6 +12571,10 @@ function uniqueToken() {
12571
12571
  }
12572
12572
  // ../../packages/kk-core/src/render-md.ts
12573
12573
  if (false) {}
12574
+ // ../../packages/kk-core/src/sentences.ts
12575
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
12576
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
12577
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
12574
12578
  // ../../plugins/ingestions/aihot-ingest/src/index.ts
12575
12579
  init_dist();
12576
12580
  init_zod();
@@ -12320,6 +12320,10 @@ function uniqueToken() {
12320
12320
  }
12321
12321
  // ../../packages/kk-core/src/render-md.ts
12322
12322
  if (false) {}
12323
+ // ../../packages/kk-core/src/sentences.ts
12324
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
12325
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
12326
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
12323
12327
  // ../../plugins/ingestions/horizon-ingest/src/index.ts
12324
12328
  init_dist();
12325
12329
  init_zod();
@@ -11983,7 +11983,7 @@ var init_file_system_node = () => {};
11983
11983
  // ../../plugins/ingestions/last30days-ingest/src/index.ts
11984
11984
  import { createHash } from "crypto";
11985
11985
  import { homedir } from "os";
11986
- import { dirname as dirname2, join } from "path";
11986
+ import { dirname as dirname2, join, resolve } from "path";
11987
11987
  import { parseArgs } from "util";
11988
11988
 
11989
11989
  // ../../packages/kk-core/src/content-limits.ts
@@ -12321,6 +12321,10 @@ function uniqueToken() {
12321
12321
  }
12322
12322
  // ../../packages/kk-core/src/render-md.ts
12323
12323
  if (false) {}
12324
+ // ../../packages/kk-core/src/sentences.ts
12325
+ var CJK_TERMINATORS = new Set(["\u3002", "\uFF01", "\uFF1F"]);
12326
+ var LATIN_TERMINATORS = new Set([".", "!", "?"]);
12327
+ var ATTACHED_CLOSERS = new Set(["\u201D", "\u2019", "\u300D", "\u300F", "\uFF09", ")", '"', "'"]);
12324
12328
  // ../../plugins/ingestions/last30days-ingest/src/index.ts
12325
12329
  init_dist();
12326
12330
  init_zod();
@@ -12332,6 +12336,9 @@ var InputConfigSchema = object({
12332
12336
  });
12333
12337
  var defaultRunner = async (config, outputPath) => {
12334
12338
  const skillDir = process.env.LAST30DAYS_SKILL_DIR ?? join(homedir(), ".agents", "skills", "last30days");
12339
+ if (!createNodeFileSystem().exists(skillDir)) {
12340
+ throw new Error(`skill dir missing at ${skillDir} \u2014 restore the last30days skill checkout/symlink or set LAST30DAYS_SKILL_DIR`);
12341
+ }
12335
12342
  const argv = [
12336
12343
  process.env.LAST30DAYS_PYTHON ?? "python3",
12337
12344
  join(skillDir, "scripts", "last30days.py"),
@@ -12391,7 +12398,8 @@ function mapResultsToDocs(agentJson, topic) {
12391
12398
  }
12392
12399
  async function processIngestionIO(options, config, runner = defaultRunner) {
12393
12400
  const fs = createNodeFileSystem();
12394
- const outDir = dirname2(options.out);
12401
+ const out = resolve(options.out);
12402
+ const outDir = dirname2(out);
12395
12403
  if (outDir && outDir !== ".")
12396
12404
  await fs.ensureDir(outDir);
12397
12405
  const capturePath = join(outDir || ".", ".last30days-agent.json");
@@ -12400,7 +12408,7 @@ async function processIngestionIO(options, config, runner = defaultRunner) {
12400
12408
  throw new Error(`last30days exited ${run.exitCode}: ${run.stderr.trim() || "no stderr"}`);
12401
12409
  }
12402
12410
  const docs = mapResultsToDocs(await readJsonFile(capturePath), config.topic);
12403
- await atomicWriteJson(options.out, docs, fs);
12411
+ await atomicWriteJson(out, docs, fs);
12404
12412
  return { docs };
12405
12413
  }
12406
12414
  async function main2(runner) {