@gobing-ai/knowledge-kit 0.0.17 → 0.0.19

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 (76) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +74 -0
  3. package/dist/index.js +108 -107
  4. package/package.json +19 -1
  5. package/plugins/generations/content-gen/dist/index.js +42 -94
  6. package/plugins/generations/core-facts-gen/dist/index.js +51 -100
  7. package/plugins/generations/daily-article-gen/dist/index.js +57 -99
  8. package/plugins/generations/daily-article-gen/src/index.ts +12 -4
  9. package/plugins/generations/dailynews-gen/dist/index.js +57 -102
  10. package/plugins/generations/dailynews-gen/src/script-builder.ts +9 -1
  11. package/plugins/generations/episode-plan-gen/dist/index.js +120 -113
  12. package/plugins/generations/episode-plan-gen/src/index.ts +102 -22
  13. package/plugins/generations/image-gen/dist/index.js +63 -115
  14. package/plugins/generations/news-report-gen/dist/index.js +117 -99
  15. package/plugins/generations/news-report-gen/src/index.ts +117 -1
  16. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/pipeline.py +1 -1
  17. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/voicescript.py +8 -0
  18. package/plugins/generations/voice-gen/dist/index.js +161 -120
  19. package/plugins/generations/voice-gen/src/concat.ts +34 -0
  20. package/plugins/generations/voice-gen/src/index.ts +14 -4
  21. package/plugins/generations/voice-gen/src/voicebox-server.ts +91 -0
  22. package/plugins/generations/voice-gen/src/voicescript.ts +8 -0
  23. package/plugins/ingestions/aihot-ingest/dist/index.js +47 -97
  24. package/plugins/ingestions/horizon-ingest/dist/index.js +47 -96
  25. package/plugins/ingestions/karakeep-local/dist/index.js +217 -266
  26. package/plugins/ingestions/last30days-ingest/dist/index.js +42 -91
  27. package/plugins/ingestions/web-search/dist/index.js +68 -117
  28. package/plugins/kk/commands/workflow-run.md +10 -6
  29. package/plugins/kk/plugin.json +1 -1
  30. package/plugins/kk/scripts/kk-workflow-stages.ts +376 -70
  31. package/plugins/kk/skills/publish/SKILL.md +80 -0
  32. package/plugins/kk/workflows/kk-daily-ai-voice.yaml +136 -224
  33. package/plugins/kk/workflows/leftover-publish-fanout-example.yaml +53 -0
  34. package/plugins/publishings/emdash-pub/dist/index.js +42 -91
  35. package/plugins/publishings/infoq-pub/dist/index.js +21994 -0
  36. package/plugins/publishings/infoq-pub/package.json +17 -0
  37. package/plugins/publishings/infoq-pub/plugin.json +7 -0
  38. package/plugins/publishings/infoq-pub/src/index.ts +123 -0
  39. package/plugins/publishings/infoq-pub/tsconfig.json +4 -0
  40. package/plugins/publishings/juejin-pub/dist/index.js +21994 -0
  41. package/plugins/publishings/juejin-pub/package.json +17 -0
  42. package/plugins/publishings/juejin-pub/plugin.json +7 -0
  43. package/plugins/publishings/juejin-pub/src/index.ts +123 -0
  44. package/plugins/publishings/juejin-pub/tsconfig.json +4 -0
  45. package/plugins/publishings/medium-pub/dist/index.js +22041 -0
  46. package/plugins/publishings/medium-pub/package.json +17 -0
  47. package/plugins/publishings/medium-pub/plugin.json +7 -0
  48. package/plugins/publishings/medium-pub/src/index.ts +215 -0
  49. package/plugins/publishings/medium-pub/tsconfig.json +4 -0
  50. package/plugins/publishings/podcast-pub/dist/index.js +183 -123
  51. package/plugins/publishings/podcast-pub/src/index.ts +47 -3
  52. package/plugins/publishings/podcast-pub/src/show-notes.ts +153 -34
  53. package/plugins/publishings/qiita-pub/dist/index.js +42 -91
  54. package/plugins/publishings/substack-pub/dist/index.js +21994 -0
  55. package/plugins/publishings/substack-pub/package.json +17 -0
  56. package/plugins/publishings/substack-pub/plugin.json +7 -0
  57. package/plugins/publishings/substack-pub/src/index.ts +123 -0
  58. package/plugins/publishings/substack-pub/tsconfig.json +4 -0
  59. package/plugins/publishings/surfdash-pub/dist/index.js +172 -103
  60. package/plugins/publishings/surfdash-pub/src/index.ts +32 -2
  61. package/plugins/publishings/wechatmp-pub/dist/index.js +22119 -0
  62. package/plugins/publishings/wechatmp-pub/package.json +17 -0
  63. package/plugins/publishings/wechatmp-pub/plugin.json +7 -0
  64. package/plugins/publishings/wechatmp-pub/src/index.ts +304 -0
  65. package/plugins/publishings/wechatmp-pub/tsconfig.json +4 -0
  66. package/plugins/publishings/x-pub/dist/index.js +22110 -0
  67. package/plugins/publishings/x-pub/package.json +17 -0
  68. package/plugins/publishings/x-pub/plugin.json +7 -0
  69. package/plugins/publishings/x-pub/src/index.ts +222 -0
  70. package/plugins/publishings/x-pub/tsconfig.json +4 -0
  71. package/plugins/publishings/xhs-pub/dist/index.js +21994 -0
  72. package/plugins/publishings/xhs-pub/package.json +17 -0
  73. package/plugins/publishings/xhs-pub/plugin.json +7 -0
  74. package/plugins/publishings/xhs-pub/src/index.ts +123 -0
  75. package/plugins/publishings/xhs-pub/tsconfig.json +4 -0
  76. package/plugins/publishings/zenn-pub/dist/index.js +42 -91
@@ -19,7 +19,12 @@ vars:
19
19
  limit: "10"
20
20
  language: "zh"
21
21
  voice_profile: "robin-news"
22
- voice_generator: "omni-voice-gen"
22
+ # 20260917: voice driver switch — ominivoice (default, omni-voice-gen) | voicebox (voice-gen).
23
+ vdriver: "ominivoice"
24
+ # 20260917 review #7: 8s intro sting prepended to every daily output; empty = skip.
25
+ intro_music: ".works/kk-daily-ai-voice/AI每日资讯_8秒播客片头.mp3"
26
+ # 20260917 review #4/#6: surfdash checkout the patch stage commits to.
27
+ surfdash_root: "/Users/robin/xprojects/surfdash"
23
28
  title: ""
24
29
  cursor: ""
25
30
  state_file: ""
@@ -28,17 +33,23 @@ vars:
28
33
  cover_enabled: "true"
29
34
  plan_max_items: "20"
30
35
  plan_translate_enabled: "true"
36
+ article_write_enabled: "true"
31
37
  plugins_path: ""
38
+ # Projected by the publish state from 3-publish/<run_date>_17_publish_publish-status.txt;
39
+ # guards test this var instead of cat-ing the file (state at decision time).
40
+ publish_status: ""
32
41
  last30days_topic: "AI news"
33
42
  horizon_hours: "24"
34
43
  publish_enabled: "false"
35
44
  last30days_enabled: "false"
36
45
  run_date: ""
37
46
  agent: "inline"
38
- surfdash_post_path: ""
39
47
  surfdash_en_post_path: ""
40
48
  surfdash_ja_post_path: ""
41
- qc_categories: "ai-news,ai-industry"
49
+ # 20260917 funnel: allowlist widened to the whole AI taxonomy in lockstep with
50
+ # episode-plan-gen's DEFAULT_FILTER_CONFIG — genuine technical/research stories died on
51
+ # category:not-in-allowlist. `non-ai` stays excluded (that is the point of the gate).
52
+ qc_categories: "ai-news,ai-technical,ai-research,ai-industry"
42
53
  # 0098 dogfood calibration (r7, owner-approved 0099): quality 3→2, impact 2→1 — the shipped
43
54
  # defaults rejected 20/20 live candidates (strongest ai-news items died solely on impact:1<2).
44
55
  # These pins must move in lockstep with episode-plan-gen's DEFAULT_FILTER_CONFIG (env wins).
@@ -54,13 +65,18 @@ states:
54
65
  onEnter:
55
66
  - kind: shell
56
67
  options:
68
+ # run_date (dashed or digits; the job worker expands $(date +%F) before spur sees the
69
+ # JSON — var values reach non-shell steps verbatim) only names the workspace:
70
+ # daily-prepare composes the real dir as works_dir/kk-daily-ai-voice/<stamp> when
71
+ # work_dir is empty and always writes the effective work_dir to the cwd-relative
72
+ # anchor below. Shell actions cannot set vars, so the reads project it (then the
73
+ # digits-only stamp) into vars before any later state interpolates them.
57
74
  command: >-
58
- kbin="$(command -v kk 2>/dev/null)";
59
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
60
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
61
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
62
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
63
- bun "$st" daily-prepare "${vars.work_dir}" "${vars.limit}" "${vars.cursor}" "${vars.state_file}" "${vars.last30days_enabled}" "${vars.last30days_topic}" "${vars.horizon_hours}"
75
+ kk stage daily-prepare "${vars.work_dir}" "${vars.limit}" "${vars.cursor}" "${vars.state_file}" "${vars.last30days_enabled}" "${vars.last30days_topic}" "${vars.horizon_hours}" "${vars.run_date}"
76
+ - kind: file.read.into-var
77
+ options:
78
+ path: .spur/runs/kk-daily-ai-voice/work-dir.txt
79
+ var: work_dir
64
80
  - kind: file.read.into-var
65
81
  options:
66
82
  path: ${vars.work_dir}/run-date.txt
@@ -81,12 +97,7 @@ states:
81
97
  - kind: shell
82
98
  options:
83
99
  command: >-
84
- kbin="$(command -v kk 2>/dev/null)";
85
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
86
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
87
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
88
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
89
- bun "$st" daily-collect-facts "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
100
+ kk stage daily-collect-facts "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
90
101
 
91
102
  - id: plan
92
103
  description: "Score/dedup/select + annotate the blended Doc[] into the episode plan (episode-plan-gen; annotate rides inside planEpisode)"
@@ -94,12 +105,7 @@ states:
94
105
  - kind: shell
95
106
  options:
96
107
  command: >-
97
- kbin="$(command -v kk 2>/dev/null)";
98
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
99
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
100
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
101
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
102
- bun "$st" daily-plan "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.plan_max_items}"
108
+ kk stage daily-plan "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.plan_max_items}"
103
109
 
104
110
  - id: quality-control-content
105
111
  description: "Content QC: filter the plan's annotated candidates by category allowlist + score thresholds (episode-plan-gen --mode filter); zero candidates route to done-no-content"
@@ -107,12 +113,7 @@ states:
107
113
  - kind: shell
108
114
  options:
109
115
  command: >-
110
- kbin="$(command -v kk 2>/dev/null)";
111
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
112
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
113
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
114
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
115
- bun "$st" daily-qc-content "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.qc_categories}" "${vars.qc_min_quality}" "${vars.qc_min_importance}" "${vars.qc_min_urgency}" "${vars.qc_min_impact}"
116
+ kk stage daily-qc-content "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.qc_categories}" "${vars.qc_min_quality}" "${vars.qc_min_importance}" "${vars.qc_min_urgency}" "${vars.qc_min_impact}"
116
117
 
117
118
  - id: plan-translate
118
119
  description: "Translate needsTranslation plan items into the output plan.json via agent.run"
@@ -124,26 +125,37 @@ states:
124
125
  # ADR-115 error tier: keep this prompt <= 1000 chars (0133 R3; 0138 R4a adds the copy discipline).
125
126
  input: |
126
127
  Read ${vars.work_dir}/2-plan/${vars.run_date}_04_quality-control-content_candidates.json — metadata.docs (Doc[]) is the plan; don't edit it.
127
- For items with needsTranslation, translate "title" and "body" into natural, concise simplified Chinese.
128
- Titles read as a news event (subject + action + object) from the body — e.g. "OpenAI 发布 Sora 应用,集成 Sora 2 模型". Keep product/model/company names verbatim, preserve body URLs, use 「」 not ASCII double quotes.
128
+ For items with needsTranslation, translate "title", "body", and "metadata.brief" (when present) into natural simplified Chinese.
129
+ Titles read as a news event (subject + action + object) — e.g. "OpenAI 发布 Sora 应用,集成 Sora 2 模型". Keep product/model/company names verbatim, preserve body URLs, use 「」 not ASCII double quotes.
129
130
  Copy discipline: reader-facing news copy — no scaffold labels (「可做角度」「为什么现在值得注意」「社区讨论」「背景」「潜在影响」「影响」), no duplicated labels or hedging (「读者宜另行核实」「仍待验证」) — merge their substance into prose or drop.
130
131
  Keep other fields (id, sourceUri, mediaType, metadata), technical terms as-is; no trailing commas or comments.
131
132
  Output strictly valid JSON — a same-length, same-order array with the same ids — to ${vars.work_dir}/2-plan/${vars.run_date}_03_plan_plan.json.
132
133
  expectFile: ${vars.work_dir}/2-plan/${vars.run_date}_03_plan_plan.json
133
134
  allowAppend: false
134
135
 
136
+ - id: article-write
137
+ description: "Write the morning-news prose article from the translated plan via agent.run (3-format split: surfdash gets prose, not a news list)"
138
+ onEnter:
139
+ - kind: agent.run
140
+ options:
141
+ role: scribe
142
+ agent: ${vars.agent}
143
+ # ADR-115 error tier: keep this prompt <= 1000 chars (0133 R3).
144
+ input: |
145
+ Read ${vars.work_dir}/2-plan/${vars.run_date}_03_plan_plan.json — metadata.docs (Doc[]) is the translated plan; don't edit it.
146
+ 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.
147
+ 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.
148
+ Output the markdown to ${vars.work_dir}/2-article/${vars.run_date}_06_article-write_body.md.
149
+ expectFile: ${vars.work_dir}/2-article/${vars.run_date}_06_article-write_body.md
150
+ allowAppend: false
151
+
135
152
  - id: article
136
153
  description: "Generate the dated daily article from the episode plan (daily-article-gen template)"
137
154
  onEnter:
138
155
  - kind: shell
139
156
  options:
140
157
  command: >-
141
- kbin="$(command -v kk 2>/dev/null)";
142
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
143
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
144
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
145
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
146
- bun "$st" daily-article "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
158
+ kk stage daily-article "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
147
159
 
148
160
  - id: cover
149
161
  description: "Generate the article cover image via the kk:image-authoring skill (agent.run)"
@@ -162,12 +174,7 @@ states:
162
174
  - kind: shell
163
175
  options:
164
176
  command: >-
165
- kbin="$(command -v kk 2>/dev/null)";
166
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
167
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
168
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
169
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
170
- bun "$st" daily-cover-normalize "${vars.work_dir}" "${vars.run_date}"
177
+ kk stage daily-cover-normalize "${vars.work_dir}" "${vars.run_date}"
171
178
 
172
179
  - id: script
173
180
  description: "Compile the episode plan Doc[] into structured broadcast VoiceScript YAML Content"
@@ -175,12 +182,7 @@ states:
175
182
  - kind: shell
176
183
  options:
177
184
  command: >-
178
- kbin="$(command -v kk 2>/dev/null)";
179
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
180
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
181
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
182
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
183
- bun "$st" daily-script "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.language}" "${vars.title}" "${vars.voice_profile}"
185
+ kk stage daily-script "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.language}" "${vars.title}" "${vars.voice_profile}"
184
186
 
185
187
  - id: review
186
188
  description: "Operator reviews the generated news script before speech synthesis"
@@ -197,25 +199,23 @@ states:
197
199
  - kind: shell
198
200
  options:
199
201
  command: >-
200
- kbin="$(command -v kk 2>/dev/null)";
201
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
202
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
203
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
204
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
205
- bun "$st" daily-wrap-docs "${vars.work_dir}" "${vars.run_date}" "${vars.voice_profile}"
202
+ kk stage daily-wrap-docs "${vars.work_dir}" "${vars.run_date}" "${vars.voice_profile}"
206
203
 
207
204
  - id: generate
208
- description: "Invoke voice generator plugin (voice_generator var) to generate audio"
205
+ description: "Invoke voice driver plugin (vdriver var: ominivoice -> omni-voice-gen, voicebox -> voice-gen) to generate audio"
209
206
  onEnter:
210
207
  - kind: shell
211
208
  options:
212
209
  command: >-
213
- kbin="$(command -v kk 2>/dev/null)";
214
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
215
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
216
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
217
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
218
- bun "$st" daily-generate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.transcode_mp3}" "${vars.voice_generator}"
210
+ OMNIVOICE_SPEED=1.06 kk stage daily-generate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.transcode_mp3}" "${vars.vdriver}"
211
+
212
+ - id: intro-music
213
+ description: "Prepend the 8s intro sting (intro_music var; empty = skip) to the generated wav, update duration/audioPath"
214
+ onEnter:
215
+ - kind: shell
216
+ options:
217
+ command: >-
218
+ kk stage daily-intro "${vars.work_dir}" "${vars.run_date}" "${vars.intro_music}"
219
219
 
220
220
  - id: quality-control
221
221
  description: "Verify audio quality, speech rate, and silence/repetition QC metrics"
@@ -223,12 +223,7 @@ states:
223
223
  - kind: shell
224
224
  options:
225
225
  command: >-
226
- kbin="$(command -v kk 2>/dev/null)";
227
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
228
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
229
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
230
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
231
- bun "$st" daily-quality-report "${vars.work_dir}" "${vars.run_date}"
226
+ kk stage daily-quality-report "${vars.work_dir}" "${vars.run_date}"
232
227
 
233
228
  - id: publish-prep
234
229
  description: "Merge article Content with audio (podcast assembly facts ride the content options channel for podcast-pub's absorbed assembly; runDate kept for surfdash slug)"
@@ -236,12 +231,7 @@ states:
236
231
  - kind: shell
237
232
  options:
238
233
  command: >-
239
- kbin="$(command -v kk 2>/dev/null)";
240
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
241
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
242
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
243
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
244
- bun "$st" daily-publish-prep "${vars.work_dir}" "${vars.run_date}"
234
+ kk stage daily-publish-prep "${vars.work_dir}" "${vars.run_date}"
245
235
 
246
236
  - id: publish-surfdash
247
237
  description: "Publish the merged Content to surfdash (single target); result lands in result.json — surfdash publishes first so show-notes can link the article (task 0118 seam reorder)"
@@ -249,12 +239,7 @@ states:
249
239
  - kind: shell
250
240
  options:
251
241
  command: >-
252
- kbin="$(command -v kk 2>/dev/null)";
253
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
254
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
255
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
256
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
257
- bun "$st" daily-publish-surfdash "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
242
+ kk stage daily-publish-surfdash "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
258
243
 
259
244
  - id: show-notes
260
245
  description: "Render timeline show notes from the episode plan (podcast-pub show-notes op, briefMode; article URL from the surfdash result postPath — surfdash failure degrades to legacy full-body layout)"
@@ -262,12 +247,7 @@ states:
262
247
  - kind: shell
263
248
  options:
264
249
  command: >-
265
- kbin="$(command -v kk 2>/dev/null)";
266
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
267
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
268
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
269
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
270
- bun "$st" daily-show-notes "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.title}"
250
+ kk stage daily-show-notes "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.title}"
271
251
 
272
252
  - id: publish-podcast
273
253
  description: "Publish the episode to podcast-pub with the rendered show notes (metadata.showNotes carries the brief/timeline markdown; map.ts falls back to content.body when absent — task 0118 R3)"
@@ -275,12 +255,7 @@ states:
275
255
  - kind: shell
276
256
  options:
277
257
  command: >-
278
- kbin="$(command -v kk 2>/dev/null)";
279
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
280
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
281
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
282
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
283
- bun "$st" daily-publish-podcast "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
258
+ kk stage daily-publish-podcast "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
284
259
 
285
260
  - id: publish
286
261
  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)"
@@ -288,12 +263,19 @@ states:
288
263
  - kind: shell
289
264
  options:
290
265
  command: >-
291
- kbin="$(command -v kk 2>/dev/null)";
292
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
293
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
294
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
295
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
296
- bun "$st" daily-publish-classify "${vars.work_dir}" "${vars.run_date}"
266
+ kk stage daily-publish-classify "${vars.work_dir}" "${vars.run_date}"
267
+ - kind: file.read.into-var
268
+ options:
269
+ path: ${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt
270
+ var: publish_status
271
+
272
+ - id: patch-surfdash
273
+ description: "20260917 #4/#6 — patch published surfdash posts: podcast cover URL into image/og_image frontmatter + podcast cross-link (zh/en/ja), commit+push"
274
+ onEnter:
275
+ - kind: shell
276
+ options:
277
+ command: >-
278
+ kk stage daily-patch-surfdash "${vars.work_dir}" "${vars.run_date}" "${vars.surfdash_root}"
297
279
 
298
280
  - id: publish-partial
299
281
  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)"
@@ -301,12 +283,7 @@ states:
301
283
  - kind: shell
302
284
  options:
303
285
  command: >-
304
- kbin="$(command -v kk 2>/dev/null)";
305
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
306
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
307
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
308
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
309
- bun "$st" daily-publish-partial "${vars.work_dir}" "${vars.run_date}"
286
+ kk stage daily-publish-partial "${vars.work_dir}" "${vars.run_date}"
310
287
 
311
288
  - id: run-report
312
289
  description: "Assemble the per-run report (scores+aggregates+rejected counts+step timing) from existing artifacts — news-report-gen; output-only, never throws; 0117"
@@ -314,91 +291,38 @@ states:
314
291
  - kind: shell
315
292
  options:
316
293
  command: >-
317
- kbin="$(command -v kk 2>/dev/null)";
318
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
319
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
320
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
321
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
322
- bun "$st" daily-run-report "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.qc_min_quality}" "${vars.qc_min_importance}" "${vars.qc_min_urgency}" "${vars.qc_min_impact}"
323
-
324
- - id: translate-en
325
- description: "Scaffold en draft from the zh article via surfdash-pub scaffold-locale (postPath from publish result; done-no-content/none-publish runs carry no zh path and skip)"
294
+ kk stage daily-run-report "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.qc_min_quality}" "${vars.qc_min_importance}" "${vars.qc_min_urgency}" "${vars.qc_min_impact}"
295
+
296
+ - id: translate
297
+ description: "Locales — surfdash-pub scaffolds en/ja drafts (deterministic, plugin-owned), agent translates both, sync into the surfdash CMS"
326
298
  onEnter:
327
299
  - kind: shell
328
300
  options:
329
301
  command: >-
330
- kbin="$(command -v kk 2>/dev/null)";
331
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
332
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
333
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
334
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
335
- bun "$st" daily-translate-en "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
336
- - kind: file.read.into-var
337
- options:
338
- path: ${vars.work_dir}/3-publish/${vars.run_date}_20_translate-en_zh-post-path.txt
339
- var: surfdash_post_path
302
+ kk stage daily-translate-localize "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
340
303
  - kind: file.read.into-var
341
304
  options:
342
- path: ${vars.work_dir}/3-publish/${vars.run_date}_20_translate-en_en-post-path.txt
305
+ path: ${vars.work_dir}/3-publish/${vars.run_date}_20_translate-localize_en-post-path.txt
343
306
  var: surfdash_en_post_path
344
-
345
- - id: translate-en-agent
346
- description: "Translate the en draft in place via agent.run"
347
- onEnter:
348
- - kind: agent.run
349
- options:
350
- role: scribe
351
- agent: ${vars.agent}
352
- input: |
353
- Translate the English draft post at ${vars.surfdash_en_post_path} from Chinese to English.
354
- Edit the file in place: translate the title in frontmatter and the whole markdown body to natural English. Keep frontmatter keys, dates, URLs, code, and the assets/ image reference untouched. Keep the draft: true flag.
355
- Read the surfdash translator glossary if present at the surfdash checkout root under .agents/skills/surfdash-translator/glossary.md and apply its terminology.
356
- Do not wait for operator confirmation. The file must end fully in English.
357
- expectFile: ${vars.surfdash_en_post_path}
358
-
359
- - id: translate-ja
360
- description: "Scaffold ja draft from the zh article via surfdash-pub scaffold-locale (sed-derived path as cache pre-check; plugin's draftPath is authoritative once scaffolded)"
361
- onEnter:
362
- - kind: shell
363
- options:
364
- command: >-
365
- kbin="$(command -v kk 2>/dev/null)";
366
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
367
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
368
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
369
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
370
- bun "$st" daily-translate-ja "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
371
307
  - kind: file.read.into-var
372
308
  options:
373
- path: ${vars.work_dir}/3-publish/${vars.run_date}_22_translate-ja_ja-post-path.txt
309
+ path: ${vars.work_dir}/3-publish/${vars.run_date}_20_translate-localize_ja-post-path.txt
374
310
  var: surfdash_ja_post_path
375
-
376
- - id: translate-ja-agent
377
- description: "Translate the ja draft in place via agent.run"
378
- onEnter:
379
311
  - kind: agent.run
380
312
  options:
381
313
  role: scribe
382
314
  agent: ${vars.agent}
383
315
  input: |
384
- Translate the Japanese draft post at ${vars.surfdash_ja_post_path} from Chinese to Japanese.
385
- Edit the file in place: translate the title in frontmatter and the whole markdown body to natural Japanese. Keep frontmatter keys, dates, URLs, code, and the assets/ image reference untouched. Keep the draft: true flag.
386
- Read the surfdash translator glossary if present at the surfdash checkout root under .agents/skills/surfdash-translator/glossary.md and apply its terminology.
387
- Do not wait for operator confirmation. The file must end fully in Japanese.
388
- expectFile: ${vars.surfdash_ja_post_path}
389
-
390
- - id: translate-sync
391
- description: "Sync the zh post and its en/ja locale variants into the surfdash CMS (surfdash-pub sync operation)"
392
- onEnter:
316
+ Translate en/ja draft posts of the zh article — two independent files:
317
+ English draft: ${vars.surfdash_en_post_path}
318
+ Japanese draft: ${vars.surfdash_ja_post_path}
319
+ For each: if its path above is empty, skip that locale. Otherwise edit the file in place — translate the frontmatter title and the whole markdown body to natural English/Japanese respectively. Keep frontmatter keys, dates, URLs, code, and the assets/ image reference untouched. Set draft: false (the translated post publishes to the site) and remove any canonical: frontmatter line pointing at a different locale's URL.
320
+ Keep frontmatter valid YAML: double-quote any value containing ': '. Read the surfdash translator glossary if present at the surfdash checkout root under .agents/skills/surfdash-translator/glossary.md and apply its terminology.
321
+ Do not wait for operator confirmation. Each edited file must end fully in its target language.
393
322
  - kind: shell
394
323
  options:
395
324
  command: >-
396
- kbin="$(command -v kk 2>/dev/null)";
397
- pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
398
- st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
399
- [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
400
- [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
401
- bun "$st" daily-translate-sync "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.surfdash_post_path}"
325
+ kk stage daily-translate-sync "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.work_dir}/3-publish/${vars.run_date}_20_translate-localize_zh-post-path.txt"
402
326
 
403
327
  - id: done
404
328
  description: "Terminal — Daily AI news pipeline completed (collect → QC'd plan → article/cover/script/voice → publish/locales)"
@@ -444,17 +368,41 @@ transitions:
444
368
  options:
445
369
  command: 'test "${vars.plan_translate_enabled}" = "true"'
446
370
 
371
+ - from: quality-control-content
372
+ to: article-write
373
+ description: "Candidates passed QC, plan-translate disabled, prose enabled -> straight to article-write"
374
+ guard:
375
+ kind: shell
376
+ options:
377
+ command: 'test "${vars.plan_translate_enabled}" != "true" && test "${vars.article_write_enabled}" = "true"'
378
+
447
379
  - from: quality-control-content
448
380
  to: article
449
- description: "Candidates passed QC, plan-translate disabled -> straight to article"
381
+ description: "Prose disabled -> package the template article directly"
382
+ guard:
383
+ kind: shell
384
+ options:
385
+ command: 'test "${vars.article_write_enabled}" != "true"'
386
+
387
+ - from: plan-translate
388
+ to: article-write
389
+ description: "Plan translated and prose enabled -> write prose article from plan"
450
390
  guard:
451
391
  kind: shell
452
392
  options:
453
- command: 'test "${vars.plan_translate_enabled}" != "true"'
393
+ command: 'test "${vars.article_write_enabled}" = "true"'
454
394
 
455
395
  - from: plan-translate
456
396
  to: article
457
- description: "Plan translated -> generate article from plan"
397
+ description: "Plan translated, prose disabled -> package the template article"
398
+ guard:
399
+ kind: shell
400
+ options:
401
+ command: 'test "${vars.article_write_enabled}" != "true"'
402
+
403
+ - from: article-write
404
+ to: article
405
+ description: "Prose body written -> package article content"
458
406
  guard:
459
407
  kind: always
460
408
 
@@ -570,12 +518,16 @@ transitions:
570
518
  kind: always
571
519
 
572
520
  - from: publish
521
+ to: patch-surfdash
522
+ description: "Publish classified -> patch surfdash posts with cover + podcast cross-link"
523
+ guard:
524
+ kind: always
525
+
526
+ - from: patch-surfdash
573
527
  to: run-report
574
- description: "Publish full (all targets published) -> assemble run report first"
528
+ description: "Posts patched -> assemble run report"
575
529
  guard:
576
- kind: shell
577
- options:
578
- command: 'test "$(cat "${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt")" = "full"'
530
+ kind: always
579
531
 
580
532
  - from: publish
581
533
  to: publish-partial
@@ -583,7 +535,7 @@ transitions:
583
535
  guard:
584
536
  kind: shell
585
537
  options:
586
- command: 'test "$(cat "${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt")" = "partial"'
538
+ command: 'test "${vars.publish_status}" = "partial"'
587
539
 
588
540
  - from: publish
589
541
  to: failed
@@ -591,7 +543,7 @@ transitions:
591
543
  guard:
592
544
  kind: shell
593
545
  options:
594
- command: 'test "$(cat "${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt")" = "none"'
546
+ command: 'test "${vars.publish_status}" = "none"'
595
547
 
596
548
  - from: publish-partial
597
549
  to: run-report
@@ -600,12 +552,12 @@ transitions:
600
552
  kind: always
601
553
 
602
554
  - from: run-report
603
- to: translate-en
604
- description: "Run report assembled after full publish -> scaffold + translate en locale"
555
+ to: translate
556
+ description: "Run report assembled after full publish -> scaffold + translate en/ja locales + sync"
605
557
  guard:
606
558
  kind: shell
607
559
  options:
608
- command: 'test "$(cat "${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt")" = "full"'
560
+ command: 'test "${vars.publish_status}" = "full"'
609
561
 
610
562
  - from: run-report
611
563
  to: done
@@ -613,50 +565,10 @@ transitions:
613
565
  guard:
614
566
  kind: shell
615
567
  options:
616
- command: 'test "$(cat "${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt")" = "partial"'
617
-
618
- - from: translate-en
619
- to: translate-en-agent
620
- description: "zh post published and en draft scaffolded -> agent translates en draft in place"
621
- guard:
622
- kind: shell
623
- options:
624
- command: 'test -n "${vars.surfdash_post_path}" && test -f "${vars.surfdash_en_post_path}"'
625
-
626
- - from: translate-en
627
- to: translate-ja
628
- description: "no zh post path -> skip en locale"
629
- guard:
630
- kind: always
631
-
632
- - from: translate-en-agent
633
- to: translate-ja
634
- description: "en draft translated -> scaffold ja locale"
635
- guard:
636
- kind: always
637
-
638
- - from: translate-ja
639
- to: translate-ja-agent
640
- description: "zh post published and ja draft scaffolded -> agent translates ja draft in place"
641
- guard:
642
- kind: shell
643
- options:
644
- command: 'test -n "${vars.surfdash_post_path}" && test -f "${vars.surfdash_ja_post_path}"'
645
-
646
- - from: translate-ja
647
- to: translate-sync
648
- description: "no zh post path -> skip ja locale"
649
- guard:
650
- kind: always
651
-
652
- - from: translate-ja-agent
653
- to: translate-sync
654
- description: "ja draft translated -> sync all locales into the CMS"
655
- guard:
656
- kind: always
568
+ command: 'test "${vars.publish_status}" = "partial"'
657
569
 
658
- - from: translate-sync
570
+ - from: translate
659
571
  to: done
660
- description: "zh post + locales synced into surfdash CMS -> episode complete"
572
+ description: "zh post + en/ja locales scaffolded, translated, and synced into the surfdash CMS -> episode complete"
661
573
  guard:
662
574
  kind: always
@@ -0,0 +1,53 @@
1
+ "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
2
+ kind: state-machine
3
+ name: leftover-publish-fanout-example
4
+ description: >-
5
+ Example: fan out two leftover file-cli channels (medium-pub, wechatmp-pub),
6
+ then sequence one agent-default CMS channel (juejin-pub) as agent.run.
7
+ kk fanout does not spawn an LLM.
8
+ initialState: fanout-file-cli
9
+ terminalStates:
10
+ - done
11
+ - failed
12
+ failureStates:
13
+ - failed
14
+ vars:
15
+ contentFile: ".spur/run/content.json"
16
+ fanoutFile: ".spur/run/fanout-result.json"
17
+ juejinFile: ".spur/run/juejin-result.json"
18
+ spurBin: "spur"
19
+ states:
20
+ - id: fanout-file-cli
21
+ description: "kk fanout over leftover file-cli channels (medium + wechatmp draft)"
22
+ onEnter:
23
+ - kind: shell
24
+ options:
25
+ command: >-
26
+ kk fanout --in ${vars.contentFile}
27
+ --targets medium-pub,wechatmp-pub
28
+ --out ${vars.fanoutFile}
29
+ - id: agent-juejin
30
+ description: "Agent-default CMS channel — workflow agent.run, not kk fanout"
31
+ onEnter:
32
+ - kind: agent.run
33
+ options:
34
+ role: coder
35
+ input: |
36
+ Invoke the kk:publish skill for juejin.
37
+ Drive ~/.config/kk/browser-profiles/juejin as driver=agent.
38
+ Then `kk executor run juejin-pub --in ${vars.contentFile} --out ${vars.juejinFile}`.
39
+ Do not embed an LLM in the plugin process. Do not call kk fanout for this channel.
40
+ expectFile: ${vars.juejinFile}
41
+ - id: done
42
+ description: "Fan-out aggregate plus agent CMS Result written"
43
+ - id: failed
44
+ description: "A file-cli target or the agent CMS step failed loud"
45
+ transitions:
46
+ - from: fanout-file-cli
47
+ to: agent-juejin
48
+ guard:
49
+ kind: always
50
+ - from: agent-juejin
51
+ to: done
52
+ guard:
53
+ kind: always