@gobing-ai/knowledge-kit 0.0.14 → 0.0.16

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 (26) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/package.json +1 -1
  3. package/plugins/generations/omni-voice-gen/README.md +9 -3
  4. package/plugins/generations/omni-voice-gen/profiles.json +5 -1
  5. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/backend.py +12 -1
  6. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/pipeline.py +18 -2
  7. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/profiles.py +8 -0
  8. package/plugins/generations/omni-voice-gen/src/omni_voice_gen/voicescript.py +0 -12
  9. package/plugins/generations/omni-voice-gen/voices/robin-news.wav +0 -0
  10. package/plugins/kk/commands/workflow-run.md +54 -22
  11. package/plugins/kk/config.example.yaml +5 -0
  12. package/plugins/kk/plugin.json +1 -1
  13. package/plugins/kk/scripts/kk-workflow-stages.ts +1226 -0
  14. package/plugins/kk/{workflows → scripts}/validate-voicescript.ts +4 -4
  15. package/plugins/kk/{workflows → scripts}/wrap-voicescript-doc.ts +7 -6
  16. package/plugins/kk/skills/audio-authoring/SKILL.md +11 -12
  17. package/plugins/kk/skills/itc-generating/SKILL.md +2 -2
  18. package/plugins/kk/skills/itc-generating/references/generic-craft.md +1 -1
  19. package/plugins/kk/skills/itc-generating/references/platform-english.md +1 -1
  20. package/plugins/kk/skills/itc-generating/references/platform-wechat.md +1 -1
  21. package/plugins/kk/skills/storm-research/SKILL.md +5 -5
  22. package/plugins/kk/skills/topic/SKILL.md +7 -7
  23. package/plugins/kk/workflows/kk-daily-ai-voice.yaml +149 -163
  24. package/plugins/kk/workflows/kk-itc.yaml +12 -29
  25. package/plugins/kk/workflows/kk-solo-podcast.yaml +72 -131
  26. package/plugins/kk/workflows/kk-storm-research.yaml +12 -62
@@ -19,7 +19,7 @@ vars:
19
19
  limit: "10"
20
20
  language: "zh"
21
21
  voice_profile: "robin-news"
22
- voice_generator: "voice-gen"
22
+ voice_generator: "omni-voice-gen"
23
23
  title: ""
24
24
  cursor: ""
25
25
  state_file: ""
@@ -34,7 +34,7 @@ vars:
34
34
  publish_enabled: "false"
35
35
  last30days_enabled: "false"
36
36
  run_date: ""
37
- agent: "auto"
37
+ agent: "inline"
38
38
  surfdash_post_path: ""
39
39
  surfdash_en_post_path: ""
40
40
  surfdash_ja_post_path: ""
@@ -55,14 +55,12 @@ states:
55
55
  - kind: shell
56
56
  options:
57
57
  command: >-
58
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"prepare\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
59
- test -n "${vars.work_dir}" || { echo "missing required variable: work_dir" >&2 && exit 1; };
60
- mkdir -p "${vars.work_dir}/1-ingest" "${vars.work_dir}/2-facts" "${vars.work_dir}/2-plan" "${vars.work_dir}/2-article" "${vars.work_dir}/2-cover" "${vars.work_dir}/2-script" "${vars.work_dir}/3-audio" "${vars.work_dir}/3-publish";
61
- date +%Y%m%d > "${vars.work_dir}/run-date.txt" &&
62
- bun -e 'await Bun.write(process.argv[1], JSON.stringify({ limit: Number(process.argv[2]), cursor: process.argv[3] || undefined, stateFile: process.argv[4] || undefined }, null, 2))' "${vars.work_dir}/1-ingest/source.json" "${vars.limit}" "${vars.cursor}" "${vars.state_file}" &&
63
- if [ "${vars.last30days_enabled}" != "true" ]; then printf '[]' > "${vars.work_dir}/1-ingest/last30days-docs.json"; fi &&
64
- bun -e 'await Bun.write(process.argv[1], JSON.stringify({ topic: process.argv[2] }, null, 2))' "${vars.work_dir}/1-ingest/last30days.json" "${vars.last30days_topic}" &&
65
- bun -e 'await Bun.write(process.argv[1], JSON.stringify({ hours: Number(process.argv[2]) }, null, 2))' "${vars.work_dir}/1-ingest/horizon.json" "${vars.horizon_hours}"
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}"
66
64
  - kind: file.read.into-var
67
65
  options:
68
66
  path: ${vars.work_dir}/run-date.txt
@@ -78,17 +76,17 @@ states:
78
76
  # 45m leash; aihot/last30days keep the default.
79
77
  command: >-
80
78
  __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"collect\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
81
- if [ -f "${vars.work_dir}/1-ingest/blended.json" ]; then echo "collect cached: ${vars.work_dir}/1-ingest/blended.json"; exit 0; fi;
82
- kk executor fan-in --source "aihot-ingest:${vars.work_dir}/1-ingest/source.json" --source "last30days-ingest:${vars.work_dir}/1-ingest/last30days.json" --source "horizon-ingest:${vars.work_dir}/1-ingest/horizon.json" --source-timeout-override "horizon-ingest:2700000" --out "${vars.work_dir}/1-ingest/blended.json"
79
+ if [ -f "${vars.work_dir}/1-ingest/${vars.run_date}_02_collect_blended.json" ]; then echo "collect cached: ${vars.work_dir}/1-ingest/${vars.run_date}_02_collect_blended.json"; exit 0; fi;
80
+ kk executor fan-in --source "aihot-ingest:${vars.work_dir}/1-ingest/source.json" --source "last30days-ingest:${vars.work_dir}/1-ingest/last30days.json" --source "horizon-ingest:${vars.work_dir}/1-ingest/horizon.json" --source-timeout-override "horizon-ingest:2700000" --out "${vars.work_dir}/1-ingest/${vars.run_date}_02_collect_blended.json"
83
81
  - kind: shell
84
82
  options:
85
83
  command: >-
86
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"collect\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
87
- set --;
88
- if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
89
- if [ -f "${vars.work_dir}/2-facts/content.json" ] && [ -f "${vars.work_dir}/2-facts/core-facts.md" ]; then echo "core facts cached: ${vars.work_dir}/2-facts/core-facts.md"; exit 0; fi;
90
- kk executor run core-facts-gen --in "${vars.work_dir}/1-ingest/blended.json" --out "${vars.work_dir}/2-facts/content.json" "$@" &&
91
- bun -e 'await Bun.write(process.argv[2], (await Bun.file(process.argv[1]).json()).body)' "${vars.work_dir}/2-facts/content.json" "${vars.work_dir}/2-facts/core-facts.md"
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}"
92
90
 
93
91
  - id: plan
94
92
  description: "Score/dedup/select + annotate the blended Doc[] into the episode plan (episode-plan-gen; annotate rides inside planEpisode)"
@@ -96,14 +94,12 @@ states:
96
94
  - kind: shell
97
95
  options:
98
96
  command: >-
99
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"plan\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
100
- set --;
101
- if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
102
- PLAN_CONTENT="${vars.work_dir}/2-plan/content.json"; PLAN_DOCS="${vars.work_dir}/2-plan/plan.json";
103
- if [ -f "$PLAN_DOCS" ]; then echo "plan cached: $PLAN_DOCS"; exit 0; fi;
104
- EPISODE_PLAN_MAX_ITEMS="${vars.plan_max_items}" EPISODE_PLAN_DATE="${vars.run_date}" kk executor run episode-plan-gen --in "${vars.work_dir}/1-ingest/blended.json" --out "$PLAN_CONTENT" "$@" &&
105
- bun -e 'const content = JSON.parse(await Bun.file(process.argv[1]).text()); const docs = content.metadata?.docs; if (!Array.isArray(docs) || docs.length === 0) { console.error("plan: content.metadata.docs missing or empty"); process.exit(1); } await Bun.write(process.argv[2], JSON.stringify(docs, null, 2))' "$PLAN_CONTENT" "$PLAN_DOCS" &&
106
- test -f "$PLAN_DOCS" || { echo "plan: $PLAN_DOCS not written after episode-plan-gen"; exit 1; }
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}"
107
103
 
108
104
  - id: quality-control-content
109
105
  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"
@@ -111,12 +107,12 @@ states:
111
107
  - kind: shell
112
108
  options:
113
109
  command: >-
114
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"quality-control-content\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
115
- set --;
116
- if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
117
- if [ -f "${vars.work_dir}/2-plan/candidates.json" ]; then echo "candidates cached: ${vars.work_dir}/2-plan/candidates.json"; exit 0; fi;
118
- jq '.metadata.docs' "${vars.work_dir}/2-plan/content.json" > "${vars.work_dir}/2-plan/annotated.json" &&
119
- EPISODE_PLAN_MODE=filter QC_CATEGORIES="${vars.qc_categories}" QC_MIN_QUALITY="${vars.qc_min_quality}" QC_MIN_IMPORTANCE="${vars.qc_min_importance}" QC_MIN_URGENCY="${vars.qc_min_urgency}" QC_MIN_IMPACT="${vars.qc_min_impact}" kk executor run episode-plan-gen --in "${vars.work_dir}/2-plan/annotated.json" --out "${vars.work_dir}/2-plan/candidates.json" "$@"
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}"
120
116
 
121
117
  - id: plan-translate
122
118
  description: "Translate needsTranslation plan items into the output plan.json via agent.run"
@@ -126,14 +122,12 @@ states:
126
122
  role: scribe
127
123
  agent: ${vars.agent}
128
124
  input: |
129
- Read the JSON file at ${vars.work_dir}/2-plan/candidates.json. Its metadata.docs is a JSON array of news items (Doc[]) — this is the episode plan to translate. Do not edit candidates.json.
130
- For every item with needsTranslation: translate its "title" and "body" fields into natural, concise Chinese (simplified).
131
- Titles must read as a news event, not a bare label: derive the title from the item's body (first sentence carries the event) so "Sora 2" becomes e.g. "OpenAI 发布 Sora 应用,集成 Sora 2 模型" — subject + action + object. Keep product/model/company names verbatim.
132
- Keep everything else untouched: metadata (including needsTranslation, planScore, mergedFrom, urls), sourceUri, id, mediaType.
133
- Preserve any URLs inside the body text. Keep technical terms (product/model/company names) in their original form where that is clearer.
134
- The output must be strictly valid JSON: escape every double quote inside string values as \", never use raw ASCII double quotes inside Chinese text (use 「」 or \"), and produce no trailing commas or comments.
135
- Write the translated array — a valid JSON array of the same length, same item order, same ids — to ${vars.work_dir}/2-plan/plan.json (create the file).
136
- expectFile: ${vars.work_dir}/2-plan/plan.json
125
+ Read ${vars.work_dir}/2-plan/${vars.run_date}_04_quality-control-content_candidates.json — its metadata.docs (Doc[]) is the episode plan to translate. Do not edit candidates.json.
126
+ For every item with needsTranslation, translate "title" and "body" into natural, concise simplified Chinese.
127
+ Titles must read as a news event (subject + action + object), derived from the body — e.g. "OpenAI 发布 Sora 应用,集成 Sora 2 模型". Keep product/model/company names verbatim, preserve URLs inside the body, and use 「」 instead of raw ASCII double quotes inside Chinese text.
128
+ Keep every other field untouched (id, sourceUri, mediaType, metadata), keep technical terms in their original form where that is clearer, and produce no trailing commas or comments.
129
+ Output strictly valid JSON — a same-length, same-order array carrying the same ids — to ${vars.work_dir}/2-plan/${vars.run_date}_03_plan_plan.json.
130
+ expectFile: ${vars.work_dir}/2-plan/${vars.run_date}_03_plan_plan.json
137
131
  allowAppend: false
138
132
 
139
133
  - id: article
@@ -142,11 +136,12 @@ states:
142
136
  - kind: shell
143
137
  options:
144
138
  command: >-
145
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"article\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
146
- set --;
147
- if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
148
- CONTENT_OUT="${vars.work_dir}/2-article/content-${vars.run_date}.json"; ARTICLE_MD="${vars.work_dir}/2-article/article-${vars.run_date}.md";
149
- if [ -f "$ARTICLE_MD" ]; then echo "article cached: $ARTICLE_MD"; else PLAN_IN="${vars.work_dir}/2-plan/plan.json"; PLAN_OK=1; bun -e 'try { JSON.parse(await Bun.file(process.argv[1]).text()); process.exit(0); } catch (e) { console.warn("article: plan.json invalid JSON — replacing with untranslated plan (downstream states read this file): " + e.message); const docs = JSON.parse(await Bun.file(process.argv[2]).text()).metadata?.docs; if (!Array.isArray(docs) || docs.length === 0) { console.error("article: candidates.json metadata.docs missing"); process.exit(2); } await Bun.write(process.argv[1], JSON.stringify(docs, null, 2)); process.exit(1); }' "$PLAN_IN" "${vars.work_dir}/2-plan/candidates.json"; case $? in 0|1) PLAN_OK=0;; esac; test "$PLAN_OK" -eq 0 || { echo "article: no usable plan input"; exit 1; }; ARTICLE_DATE="${vars.run_date}" kk executor run daily-article-gen --in "$PLAN_IN" --out "$CONTENT_OUT" "$@" && bun -e 'await Bun.write(process.argv[2], (await Bun.file(process.argv[1]).json()).body)' "$CONTENT_OUT" "$ARTICLE_MD"; fi
139
+ kbin="$(command -v kk 2>/dev/null)";
140
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
141
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
142
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
143
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
144
+ bun "$st" daily-article "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
150
145
 
151
146
  - id: cover
152
147
  description: "Generate the article cover image via the kk:image-authoring skill (agent.run)"
@@ -157,15 +152,20 @@ states:
157
152
  agent: ${vars.agent}
158
153
  input: |
159
154
  Invoke the kk:image-authoring skill (generate operation).
160
- This node is a non-interactive workflow step: run every image command with KK_NONINTERACTIVE=1 and KK_IMAGE_AUTO_CONFIRM=1 in the environment (this workflow approves the image spend via cover_enabled) — never prompt, never open a question.
161
- Command: --format cover --content ${vars.work_dir}/2-article/article-${vars.run_date}.md --out ${vars.work_dir}/2-cover
162
- Derive the cover template variables (title, subtitle, topics) from the article as the skill owns; style is the format's default — do not pass --style.
163
- The plugin writes ${vars.work_dir}/2-cover/content.json plus the artifact ${vars.work_dir}/2-cover/content-cover-01.<ext>, where <ext> follows the elected provider's native output (payload honesty: minimax returns JPEG, google/ark return PNG). If generation fails, report the error verbatim and stop — no procedural fallback.
164
- Run 20260911 context: only MINIMAX_API_KEY is credentialed (ARK out of token, google key unset), so the elected provider is minimax and the artifact lands as content-cover-01.jpg.
155
+ Non-interactive step: run every image command with KK_NONINTERACTIVE=1 and KK_IMAGE_AUTO_CONFIRM=1 — never prompt (cover_enabled is the spend approval).
156
+ Command: --format cover --content ${vars.work_dir}/2-article/${vars.run_date}_06_article_article.md --out ${vars.work_dir}/2-cover
157
+ Template variables (title, subtitle, topics) come from the article as the skill owns; do not pass --style.
158
+ The plugin writes ${vars.work_dir}/2-cover/content.json plus ${vars.work_dir}/2-cover/content-cover-01.<ext> (ext follows the elected provider: minimax → .jpg, google/ark → .png). On failure report the error verbatim and stop — no procedural fallback.
165
159
  expectFile: ${vars.work_dir}/2-cover/content-cover-01.jpg
166
160
  - kind: shell
167
161
  options:
168
- command: 'cd "${vars.work_dir}/2-cover" && src="content-cover-01.png"; test -f "$src" || src="content-cover-01.jpg"; test -f "$src" || { echo "cover normalize: no content-cover-01.{png,jpg} artifact" >&2; exit 1; }; case "$src" in *.jpg) command -v sips >/dev/null 2>&1 || { echo "cover normalize: sips not found; cannot transcode jpg -> png" >&2; exit 1; }; sips -s format png "$src" --out "cover-${vars.run_date}.png" >/dev/null;; *) mv "$src" "cover-${vars.run_date}.png";; esac; rm -f content-cover-01.jpg content-cover-01.png; echo "cover normalized: cover-${vars.run_date}.png"'
162
+ command: >-
163
+ kbin="$(command -v kk 2>/dev/null)";
164
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
165
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
166
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
167
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
168
+ bun "$st" daily-cover-normalize "${vars.work_dir}" "${vars.run_date}"
169
169
 
170
170
  - id: script
171
171
  description: "Compile the episode plan Doc[] into structured broadcast VoiceScript YAML Content"
@@ -173,11 +173,12 @@ states:
173
173
  - kind: shell
174
174
  options:
175
175
  command: >-
176
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"script\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
177
- set --;
178
- if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
179
- SCRIPT_CONTENT_OUT="${vars.work_dir}/2-script/content-${vars.run_date}.json"; VOICESCRIPT_YAML="${vars.work_dir}/2-script/voicescript-${vars.run_date}.yaml";
180
- if [ -f "$VOICESCRIPT_YAML" ]; then echo "voicescript capped by plan: $VOICESCRIPT_YAML"; else DAILYNEWS_LANGUAGE="${vars.language}" DAILYNEWS_TITLE="${vars.title}" VOICEBOX_DEFAULT_PROFILE="${vars.voice_profile}" kk executor run dailynews-gen --in "${vars.work_dir}/2-plan/plan.json" --out "$SCRIPT_CONTENT_OUT" "$@" && bun -e 'await Bun.write(process.argv[2], (await Bun.file(process.argv[1]).json()).body)' "$SCRIPT_CONTENT_OUT" "$VOICESCRIPT_YAML"; fi
176
+ kbin="$(command -v kk 2>/dev/null)";
177
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
178
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
179
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
180
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
181
+ bun "$st" daily-script "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.language}" "${vars.title}" "${vars.voice_profile}"
181
182
 
182
183
  - id: review
183
184
  description: "Operator reviews the generated news script before speech synthesis"
@@ -185,17 +186,21 @@ states:
185
186
  onEnter:
186
187
  - kind: hitl.confirm
187
188
  options:
188
- prompt: "Review daily AI news VoiceScript at ${vars.work_dir}/2-script/voicescript-${vars.run_date}.yaml. Proceed to voice generation?"
189
+ prompt: "Review daily AI news VoiceScript at ${vars.work_dir}/2-script/${vars.run_date}_08_script_voicescript.yaml. Proceed to voice generation?"
189
190
  var: __hitlAnswer
190
191
 
191
192
  - id: wrap-docs
192
- description: "Wrap VoiceScript into Doc[] input for voice-gen"
193
+ description: "Wrap VoiceScript into Doc[] input for the voice generator plugin"
193
194
  onEnter:
194
195
  - kind: shell
195
196
  options:
196
197
  command: >-
197
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"wrap-docs\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
198
- bun -e 'const scriptUrl = new URL(process.argv[1], "file://" + process.cwd() + "/"); const scriptPath = decodeURIComponent(scriptUrl.pathname); const doc = { id: new Bun.CryptoHasher("sha256").update(scriptUrl.href).digest("hex").slice(0, 16), title: "Daily AI News VoiceScript", sourceUri: scriptUrl.href, body: await Bun.file(scriptPath).text(), mediaType: "application/yaml", metadata: { voiceProfile: process.argv[3] || process.env.VOICEBOX_DEFAULT_PROFILE || "robin-news" } }; await Bun.write(process.argv[2], JSON.stringify([doc], null, 2))' "${vars.work_dir}/2-script/voicescript-${vars.run_date}.yaml" "${vars.work_dir}/3-audio/docs.json" "${vars.voice_profile}"
198
+ kbin="$(command -v kk 2>/dev/null)";
199
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
200
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
201
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
202
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
203
+ bun "$st" daily-wrap-docs "${vars.work_dir}" "${vars.run_date}" "${vars.voice_profile}"
199
204
 
200
205
  - id: generate
201
206
  description: "Invoke voice generator plugin (voice_generator var) to generate audio"
@@ -203,11 +208,12 @@ states:
203
208
  - kind: shell
204
209
  options:
205
210
  command: >-
206
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"generate\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
207
- set --;
208
- if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
209
- if [ -f "${vars.work_dir}/3-audio/content.json" ] && jq -e '.metadata.qc.passed == true' "${vars.work_dir}/3-audio/content.json" > /dev/null 2>&1; then echo "audio cached: ${vars.work_dir}/3-audio/content.json (QC passed)"; exit 0; fi;
210
- VOICE_GEN_MP3="${vars.transcode_mp3}" kk executor run ${vars.voice_generator} --in "${vars.work_dir}/3-audio/docs.json" --out "${vars.work_dir}/3-audio/content.json" "$@"
211
+ kbin="$(command -v kk 2>/dev/null)";
212
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
213
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
214
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
215
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
216
+ bun "$st" daily-generate "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.transcode_mp3}" "${vars.voice_generator}"
211
217
 
212
218
  - id: quality-control
213
219
  description: "Verify audio quality, speech rate, and silence/repetition QC metrics"
@@ -215,8 +221,12 @@ states:
215
221
  - kind: shell
216
222
  options:
217
223
  command: >-
218
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"quality-control\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
219
- bun -e 'const content = JSON.parse(await Bun.file(process.argv[1]).text()); const qc = content.metadata?.qc; console.log("=== Voice Quality Control Report ==="); console.log("Overall Score: " + (qc ? qc.overallScore : 100) + "/100 | Passed: " + (qc ? qc.passed : true)); if (qc && qc.criticalIssues && qc.criticalIssues.length) { console.warn("QC Issues:\n" + qc.criticalIssues.map(function(i) { return " - " + i; }).join("\n")); } else { console.log("All audio segments passed quality control checks cleanly."); }' "${vars.work_dir}/3-audio/content.json"
224
+ kbin="$(command -v kk 2>/dev/null)";
225
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
226
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
227
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
228
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
229
+ bun "$st" daily-quality-report "${vars.work_dir}" "${vars.run_date}"
220
230
 
221
231
  - id: publish-prep
222
232
  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)"
@@ -224,8 +234,12 @@ states:
224
234
  - kind: shell
225
235
  options:
226
236
  command: >-
227
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"publish-prep\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
228
- bun -e 'const article = JSON.parse(await Bun.file(process.argv[1]).text()); const audio = JSON.parse(await Bun.file(process.argv[2]).text()); const audioMeta = audio.metadata ?? {}; const rawFile = audioMeta.mp3Path ?? audioMeta.audioPath; if (!rawFile) { console.error("publish-prep: voice content carries no mp3Path/audioPath"); process.exit(1); } const { resolve } = await import("node:path"); const { existsSync } = await import("node:fs"); const audioFile = resolve(rawFile); const coverPath = resolve(process.argv[4]); const merged = { ...article, metadata: { ...(article.metadata ?? {}), audioFile, runDate: process.argv[5] }, options: { audioFile, durationSec: audioMeta.duration, coverPath: existsSync(coverPath) ? coverPath : undefined } }; await Bun.write(process.argv[3], JSON.stringify(merged, null, 2))' "${vars.work_dir}/2-article/content-${vars.run_date}.json" "${vars.work_dir}/3-audio/content.json" "${vars.work_dir}/3-publish/content.json" "${vars.work_dir}/2-cover/cover-${vars.run_date}.png" "${vars.run_date}"
237
+ kbin="$(command -v kk 2>/dev/null)";
238
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
239
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
240
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
241
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
242
+ bun "$st" daily-publish-prep "${vars.work_dir}" "${vars.run_date}"
229
243
 
230
244
  - id: publish-surfdash
231
245
  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)"
@@ -233,13 +247,12 @@ states:
233
247
  - kind: shell
234
248
  options:
235
249
  command: >-
236
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"publish-surfdash\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
237
- set --;
238
- if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
239
- RESULT="${vars.work_dir}/3-publish/result-surfdash.json";
240
- if jq -e '.targets[]? | select(.target == "surfdash-pub" and .ok == true and .status == "published")' "$RESULT" > /dev/null 2>&1; then echo "publish-surfdash cached: surfdash target ok in $RESULT"; exit 0; fi;
241
- env -u SPUR_WORKFLOW_RUN_ACTIVE kk executor fan-out --content "${vars.work_dir}/3-publish/content.json" --target surfdash-pub --out "$RESULT" "$@" || true;
242
- echo "publish-surfdash: surfdash target dispatched; classification happens after podcast publish"
250
+ kbin="$(command -v kk 2>/dev/null)";
251
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
252
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
253
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
254
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
255
+ bun "$st" daily-publish-surfdash "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
243
256
 
244
257
  - id: show-notes
245
258
  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)"
@@ -247,15 +260,12 @@ states:
247
260
  - kind: shell
248
261
  options:
249
262
  command: >-
250
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"show-notes\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
251
- set --;
252
- if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
253
- TITLE_ARG="${vars.title}";
254
- EFFECTIVE_TITLE="$(jq -r '.title // ""' "${vars.work_dir}/2-article/content-${vars.run_date}.json")";
255
- if [ -z "$TITLE_ARG" ]; then TITLE_ARG="$EFFECTIVE_TITLE"; fi;
256
- RESULT="${vars.work_dir}/3-publish/result.json";
257
- POST_PATH="$(jq -r '[(.targets // [])[] | select(.target == "surfdash-pub" and .ok == true) | .metadata.postPath // empty] | first // ""' "$RESULT" 2>/dev/null || true)";
258
- PODCAST_PUB_TITLE="$TITLE_ARG" PODCAST_PUB_RUN_DATE="${vars.run_date}" PODCAST_PUB_BRIEF_MODE=true PODCAST_PUB_ARTICLE_POST_PATH="$POST_PATH" kk executor run podcast-pub --in "${vars.work_dir}/2-plan/plan.json" --out "${vars.work_dir}/3-publish/show-notes.md" "$@"
263
+ kbin="$(command -v kk 2>/dev/null)";
264
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
265
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
266
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
267
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
268
+ bun "$st" daily-show-notes "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.title}"
259
269
 
260
270
  - id: publish-podcast
261
271
  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)"
@@ -263,14 +273,12 @@ states:
263
273
  - kind: shell
264
274
  options:
265
275
  command: >-
266
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"publish-podcast\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
267
- set --;
268
- if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
269
- RESULT="${vars.work_dir}/3-publish/result-podcast.json";
270
- if jq -e '.targets[]? | select(.target == "podcast-pub" and .ok == true and .status == "published")' "$RESULT" > /dev/null 2>&1; then echo "publish-podcast cached: podcast target ok in $RESULT"; exit 0; fi;
271
- bun -e 'const content = JSON.parse(await Bun.file(process.argv[1]).text()); const notes = await Bun.file(process.argv[2]).text(); content.metadata = { ...(content.metadata ?? {}), showNotes: notes }; await Bun.write(process.argv[1], JSON.stringify(content, null, 2))' "${vars.work_dir}/3-publish/content.json" "${vars.work_dir}/3-publish/show-notes.md" &&
272
- env -u SPUR_WORKFLOW_RUN_ACTIVE kk executor fan-out --content "${vars.work_dir}/3-publish/content.json" --target podcast-pub --out "$RESULT" "$@" || true;
273
- echo "publish-podcast: podcast target dispatched"
276
+ kbin="$(command -v kk 2>/dev/null)";
277
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
278
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
279
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
280
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
281
+ bun "$st" daily-publish-podcast "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
274
282
 
275
283
  - id: publish
276
284
  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)"
@@ -278,15 +286,12 @@ states:
278
286
  - kind: shell
279
287
  options:
280
288
  command: >-
281
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"publish\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
282
- RESULT="${vars.work_dir}/3-publish/result.json"; STATUS_FILE="${vars.work_dir}/3-publish/publish-status.txt";
283
- TARGETS="$(jq -s '[(.[0].targets // []), (.[1].targets // [])] | add' "${vars.work_dir}/3-publish/result-surfdash.json" "${vars.work_dir}/3-publish/result-podcast.json" 2>/dev/null || echo '[]')";
284
- OK_COUNT="$(printf '%s' "$TARGETS" | jq '[.[]? | select(.ok == true and .status == "published")] | length')";
285
- TOTAL="$(printf '%s' "$TARGETS" | jq 'length')";
286
- if [ "$OK_COUNT" -gt 0 ] && [ "$OK_COUNT" -eq "$TOTAL" ]; then STATUS="full"; elif [ "$OK_COUNT" -gt 0 ]; then STATUS="partial"; else STATUS="none"; fi;
287
- jq -n --argjson targets "$TARGETS" --argjson ok "$OK_COUNT" --argjson total "$TOTAL" '{ok: ($ok == $total and $total > 0), targets: $targets}' > "$RESULT";
288
- printf '%s' "$STATUS" > "$STATUS_FILE";
289
- echo "publish: status=$STATUS ($OK_COUNT/$TOTAL targets published); routing is transition-owned"
289
+ kbin="$(command -v kk 2>/dev/null)";
290
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
291
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
292
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
293
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
294
+ bun "$st" daily-publish-classify "${vars.work_dir}" "${vars.run_date}"
290
295
 
291
296
  - id: publish-partial
292
297
  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)"
@@ -294,12 +299,12 @@ states:
294
299
  - kind: shell
295
300
  options:
296
301
  command: >-
297
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"publish-partial\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
298
- FAILED="$(jq -r '[.targets[]? | select(.ok != true or .status != "published") | .target] | join(",")' "${vars.work_dir}/3-publish/result.json")";
299
- if [ -z "$FAILED" ]; then FAILED="(unparsed)"; fi;
300
- echo "WARNING: partial publish — failed targets: $FAILED" >&2;
301
- mkdir -p .spur/run &&
302
- printf '%s partial publish: failed targets: %s (result.json preserved for kk executor fan-out --retry-from)\n' "${vars.run_date}" "$FAILED" > ".spur/run/publish-partial-${vars.run_date}.txt"
302
+ kbin="$(command -v kk 2>/dev/null)";
303
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
304
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
305
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
306
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
307
+ bun "$st" daily-publish-partial "${vars.work_dir}" "${vars.run_date}"
303
308
 
304
309
  - id: run-report
305
310
  description: "Assemble the per-run report (scores+aggregates+rejected counts+step timing) from existing artifacts — news-report-gen; output-only, never throws; 0117"
@@ -307,17 +312,12 @@ states:
307
312
  - kind: shell
308
313
  options:
309
314
  command: >-
310
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"run-report\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
311
- REPORT_MD="${vars.work_dir}/run-report-${vars.run_date}.md";
312
- if [ -f "$REPORT_MD" ]; then echo "run report cached: $REPORT_MD"; exit 0; fi;
313
- CANDS="${vars.work_dir}/2-plan/episode-plan.json";
314
- test -f "${vars.work_dir}/2-plan/candidates.json" || { echo "run-report: no candidates.json (run ended below plan step) — skip"; exit 0; };
315
- jq '.metadata.docs // []' "${vars.work_dir}/2-plan/candidates.json" > "$CANDS";
316
- set --;
317
- if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
318
- QC_MIN_QUALITY="${vars.qc_min_quality}" QC_MIN_IMPORTANCE="${vars.qc_min_importance}" QC_MIN_URGENCY="${vars.qc_min_urgency}" QC_MIN_IMPACT="${vars.qc_min_impact}" NEWS_REPORT_REJECTED_FILE="${vars.work_dir}/2-plan/candidates.rejected.json" NEWS_REPORT_TIMING_FILE="${vars.work_dir}/step-timing.json" NEWS_REPORT_DATE="${vars.run_date}" kk executor run news-report-gen --in "$CANDS" --out "${vars.work_dir}/run-report.json" "$@" &&
319
- bun -e 'await Bun.write(process.argv[2], (await Bun.file(process.argv[1]).json()).body)' "${vars.work_dir}/run-report.json" "$REPORT_MD" &&
320
- echo "run report written: $REPORT_MD"
315
+ kbin="$(command -v kk 2>/dev/null)";
316
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
317
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
318
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
319
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
320
+ 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}"
321
321
 
322
322
  - id: translate-en
323
323
  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)"
@@ -325,27 +325,19 @@ states:
325
325
  - kind: shell
326
326
  options:
327
327
  command: >-
328
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"translate-en\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
329
- RESULT="${vars.work_dir}/3-publish/result.json";
330
- ZH="$(jq -r '[(.targets // [])[] | select(.target == "surfdash-pub" and .ok == true) | .metadata.postPath // empty] | first // ""' "$RESULT")";
331
- case "$ZH" in *index.md) ;; *) ZH="";; esac;
332
- printf '%s' "$ZH" > "${vars.work_dir}/3-publish/zh-post-path.txt";
333
- EN="$(printf '%s' "$ZH" | sed 's|/zh/|/en/|')";
334
- printf '%s' "$EN" > "${vars.work_dir}/3-publish/en-post-path.txt";
335
- test -n "$ZH" || { echo "no zh post path — skip translate-en"; exit 0; };
336
- if [ -f "$EN" ]; then echo "en draft cached: $EN"; exit 0; fi;
337
- set --;
338
- if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
339
- jq -n --arg postPath "$ZH" --arg target en '{body: "", options: {operation: "scaffold-locale", postPath: $postPath, target: $target}}' > "${vars.work_dir}/3-publish/scaffold-en.json" &&
340
- kk executor run surfdash-pub --in "${vars.work_dir}/3-publish/scaffold-en.json" --out "${vars.work_dir}/3-publish/scaffold-en-result.json" "$@" &&
341
- jq -r '.metadata.draftPath // ""' "${vars.work_dir}/3-publish/scaffold-en-result.json" > "${vars.work_dir}/3-publish/en-post-path.txt"
328
+ kbin="$(command -v kk 2>/dev/null)";
329
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
330
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
331
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
332
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
333
+ bun "$st" daily-translate-en "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
342
334
  - kind: file.read.into-var
343
335
  options:
344
- path: ${vars.work_dir}/3-publish/zh-post-path.txt
336
+ path: ${vars.work_dir}/3-publish/${vars.run_date}_20_translate-en_zh-post-path.txt
345
337
  var: surfdash_post_path
346
338
  - kind: file.read.into-var
347
339
  options:
348
- path: ${vars.work_dir}/3-publish/en-post-path.txt
340
+ path: ${vars.work_dir}/3-publish/${vars.run_date}_20_translate-en_en-post-path.txt
349
341
  var: surfdash_en_post_path
350
342
 
351
343
  - id: translate-en-agent
@@ -368,21 +360,15 @@ states:
368
360
  - kind: shell
369
361
  options:
370
362
  command: >-
371
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"translate-ja\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
372
- ZH="$(cat "${vars.work_dir}/3-publish/zh-post-path.txt" 2>/dev/null || printf '')";
373
- JA="$(printf '%s' "$ZH" | sed 's|/zh/|/ja/|')";
374
- printf '%s' "$JA" > "${vars.work_dir}/3-publish/ja-post-path.txt";
375
- test -n "$ZH" || { echo "no zh post path — skip translate-ja"; exit 0; };
376
- if [ -f "$JA" ]; then echo "ja draft cached: $JA"; exit 0; fi;
377
- set --;
378
- if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
379
- jq -n --arg postPath "$ZH" --arg target ja '{body: "", options: {operation: "scaffold-locale", postPath: $postPath, target: $target}}' > "${vars.work_dir}/3-publish/scaffold-ja.json" &&
380
- kk executor run surfdash-pub --in "${vars.work_dir}/3-publish/scaffold-ja.json" --out "${vars.work_dir}/3-publish/scaffold-ja-result.json" "$@" &&
381
- JA="$(jq -r '.metadata.draftPath // ""' "${vars.work_dir}/3-publish/scaffold-ja-result.json")" &&
382
- printf '%s' "$JA" > "${vars.work_dir}/3-publish/ja-post-path.txt"
363
+ kbin="$(command -v kk 2>/dev/null)";
364
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
365
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
366
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
367
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
368
+ bun "$st" daily-translate-ja "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}"
383
369
  - kind: file.read.into-var
384
370
  options:
385
- path: ${vars.work_dir}/3-publish/ja-post-path.txt
371
+ path: ${vars.work_dir}/3-publish/${vars.run_date}_22_translate-ja_ja-post-path.txt
386
372
  var: surfdash_ja_post_path
387
373
 
388
374
  - id: translate-ja-agent
@@ -405,12 +391,12 @@ states:
405
391
  - kind: shell
406
392
  options:
407
393
  command: >-
408
- __STAMP_T0="$(date -u +%FT%TZ)"; trap '[ -n "${vars.work_dir}" ] && printf "{\"step\":\"translate-sync\",\"startedAt\":\"%s\",\"endedAt\":\"%s\"}\n" "$__STAMP_T0" "$(date -u +%FT%TZ)" >> "${vars.work_dir}/step-timing.json"' EXIT;
409
- test -n "${vars.surfdash_post_path}" || { echo "no zh post path — skip translate-sync"; exit 0; };
410
- set --;
411
- if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
412
- jq -n --arg postPath "${vars.surfdash_post_path}" '{body: "", options: {operation: "sync", postPath: $postPath}}' > "${vars.work_dir}/3-publish/sync.json" &&
413
- kk executor run surfdash-pub --in "${vars.work_dir}/3-publish/sync.json" --out "${vars.work_dir}/3-publish/sync-result.json" "$@"
394
+ kbin="$(command -v kk 2>/dev/null)";
395
+ pkg="$(dirname "$(dirname "$(realpath "$kbin" 2>/dev/null)")")";
396
+ st="$pkg/plugins/kk/scripts/kk-workflow-stages.ts";
397
+ [ -f "$st" ] || st="plugins/kk/scripts/kk-workflow-stages.ts";
398
+ [ -f "$st" ] || echo "kk-workflow-stages.ts not found at $st — install or refresh the kk package (bun link in a checkout)" >&2;
399
+ bun "$st" daily-translate-sync "${vars.work_dir}" "${vars.run_date}" "${vars.plugins_path}" "${vars.surfdash_post_path}"
414
400
 
415
401
  - id: done
416
402
  description: "Terminal — Daily AI news pipeline completed (collect → QC'd plan → article/cover/script/voice → publish/locales)"
@@ -446,7 +432,7 @@ transitions:
446
432
  guard:
447
433
  kind: shell
448
434
  options:
449
- command: 'test "$(jq ''.metadata.docs | length == 0'' "${vars.work_dir}/2-plan/candidates.json")" = "true"'
435
+ command: 'test "$(jq ''.metadata.docs | length == 0'' "${vars.work_dir}/2-plan/${vars.run_date}_04_quality-control-content_candidates.json")" = "true"'
450
436
 
451
437
  - from: quality-control-content
452
438
  to: plan-translate
@@ -476,7 +462,7 @@ transitions:
476
462
  guard:
477
463
  kind: shell
478
464
  options:
479
- command: 'test "${vars.cover_enabled}" = "true" && test ! -f "${vars.work_dir}/2-cover/cover-${vars.run_date}.png"'
465
+ command: 'test "${vars.cover_enabled}" = "true" && test ! -f "${vars.work_dir}/2-cover/${vars.run_date}_07_cover_cover.png"'
480
466
 
481
467
  - from: article
482
468
  to: script
@@ -538,7 +524,7 @@ transitions:
538
524
  guard:
539
525
  kind: shell
540
526
  options:
541
- command: 'test "${vars.publish_enabled}" = "true" && test "$(jq -r ''.metadata.qc.passed // true'' "${vars.work_dir}/3-audio/content.json")" = "true"'
527
+ command: 'test "${vars.publish_enabled}" = "true" && test "$(jq -r ''.metadata.qc.passed // true'' "${vars.work_dir}/3-audio/${vars.run_date}_11_generate_content.json")" = "true"'
542
528
 
543
529
  - from: quality-control
544
530
  to: done
@@ -546,7 +532,7 @@ transitions:
546
532
  guard:
547
533
  kind: shell
548
534
  options:
549
- command: 'test "${vars.publish_enabled}" != "true" && test "$(jq -r ''.metadata.qc.passed // true'' "${vars.work_dir}/3-audio/content.json")" = "true"'
535
+ command: 'test "${vars.publish_enabled}" != "true" && test "$(jq -r ''.metadata.qc.passed // true'' "${vars.work_dir}/3-audio/${vars.run_date}_11_generate_content.json")" = "true"'
550
536
 
551
537
  - from: quality-control
552
538
  to: failed
@@ -584,7 +570,7 @@ transitions:
584
570
  guard:
585
571
  kind: shell
586
572
  options:
587
- command: 'test "$(cat "${vars.work_dir}/3-publish/publish-status.txt")" = "full"'
573
+ command: 'test "$(cat "${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt")" = "full"'
588
574
 
589
575
  - from: publish
590
576
  to: publish-partial
@@ -592,7 +578,7 @@ transitions:
592
578
  guard:
593
579
  kind: shell
594
580
  options:
595
- command: 'test "$(cat "${vars.work_dir}/3-publish/publish-status.txt")" = "partial"'
581
+ command: 'test "$(cat "${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt")" = "partial"'
596
582
 
597
583
  - from: publish
598
584
  to: failed
@@ -600,7 +586,7 @@ transitions:
600
586
  guard:
601
587
  kind: shell
602
588
  options:
603
- command: 'test "$(cat "${vars.work_dir}/3-publish/publish-status.txt")" = "none"'
589
+ command: 'test "$(cat "${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt")" = "none"'
604
590
 
605
591
  - from: publish-partial
606
592
  to: run-report
@@ -614,7 +600,7 @@ transitions:
614
600
  guard:
615
601
  kind: shell
616
602
  options:
617
- command: 'test "$(cat "${vars.work_dir}/3-publish/publish-status.txt")" = "full"'
603
+ command: 'test "$(cat "${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt")" = "full"'
618
604
 
619
605
  - from: run-report
620
606
  to: done
@@ -622,7 +608,7 @@ transitions:
622
608
  guard:
623
609
  kind: shell
624
610
  options:
625
- command: 'test "$(cat "${vars.work_dir}/3-publish/publish-status.txt")" = "partial"'
611
+ command: 'test "$(cat "${vars.work_dir}/3-publish/${vars.run_date}_17_publish_publish-status.txt")" = "partial"'
626
612
 
627
613
  - from: translate-en
628
614
  to: translate-en-agent