@gobing-ai/knowledge-kit 0.0.9 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/plugins/generations/dailynews-gen/src/index.ts +2 -2
- package/plugins/generations/dailynews-gen/src/script-builder.ts +352 -43
- package/plugins/generations/voice-gen/src/concat.ts +9 -7
- package/plugins/generations/voice-gen/src/index.ts +43 -4
- package/plugins/generations/voice-gen/src/qc.ts +157 -0
- package/plugins/generations/voice-gen/src/voicebox-client.ts +40 -2
- package/plugins/generations/voice-gen/src/voicescript.ts +149 -12
- package/plugins/kk/commands/workflow-run.md +70 -48
- package/plugins/kk/skills/audio-authoring/SKILL.md +185 -0
- package/plugins/kk/skills/audio-authoring/templates/voicescript.solo.yaml +25 -0
- package/plugins/kk/workflows/judge-gated-publish-example.yaml +6 -7
- package/plugins/kk/workflows/kk-daily-ai-voice.yaml +15 -1
- package/plugins/kk/workflows/kk-ingest-generate-publish.yaml +13 -4
- package/plugins/kk/workflows/kk-itc.yaml +14 -18
- package/plugins/kk/workflows/kk-solo-podcast.yaml +135 -104
- package/plugins/kk/workflows/kk-storm-research.yaml +26 -5
- package/plugins/kk/workflows/wrap-voicescript-doc.ts +40 -0
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
name: kk-solo-podcast
|
|
4
4
|
kind: state-machine
|
|
5
5
|
description: >-
|
|
6
|
-
Solo podcast authoring pipeline
|
|
6
|
+
Solo podcast authoring pipeline over the kk:audio-authoring skill, Spur HITL gates,
|
|
7
|
+
and voice-gen audio generation
|
|
7
8
|
initialState: prepare
|
|
8
9
|
terminalStates:
|
|
9
10
|
- done
|
|
@@ -26,7 +27,7 @@ vars:
|
|
|
26
27
|
plugins_path: ""
|
|
27
28
|
validate_script: ""
|
|
28
29
|
agent: ""
|
|
29
|
-
|
|
30
|
+
wrap_script: ""
|
|
30
31
|
__hitlAnswer: ""
|
|
31
32
|
__hitlInput: ""
|
|
32
33
|
|
|
@@ -36,32 +37,33 @@ states:
|
|
|
36
37
|
onEnter:
|
|
37
38
|
- kind: shell
|
|
38
39
|
options:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
fi;
|
|
63
|
-
cp "${vars.input_file}" "${vars.work_dir}/brief.md";
|
|
40
|
+
command: >-
|
|
41
|
+
set -e;
|
|
42
|
+
if [ -z "${vars.work_dir}" ]; then
|
|
43
|
+
echo "missing work_dir" >&2; exit 1;
|
|
44
|
+
fi;
|
|
45
|
+
mkdir -p "${vars.work_dir}" "${vars.work_dir}/1-briefing" "${vars.work_dir}/2-outline" "${vars.work_dir}/3-script" "${vars.work_dir}/4-audio";
|
|
46
|
+
if [ -n "${vars.topic}" ] && [ -n "${vars.input_file}" ]; then
|
|
47
|
+
echo "XOR violation: both topic and input_file set" >&2; exit 1;
|
|
48
|
+
fi;
|
|
49
|
+
if [ -z "${vars.topic}" ] && [ -z "${vars.input_file}" ]; then
|
|
50
|
+
echo "XOR violation: neither topic nor input_file set" >&2; exit 1;
|
|
51
|
+
fi;
|
|
52
|
+
if [ -z "${vars.voice_profile}" ] && [ -z "$VOICEBOX_DEFAULT_PROFILE" ]; then
|
|
53
|
+
echo "missing voice_profile (set vars.voice_profile or VOICEBOX_DEFAULT_PROFILE)" >&2; exit 1;
|
|
54
|
+
fi;
|
|
55
|
+
if [ -f "${vars.work_dir}/brief.md" ] && [ "${vars.force}" != "true" ]; then
|
|
56
|
+
echo "initialization target already exists at ${vars.work_dir}/brief.md; pass force=true to replace" >&2; exit 1;
|
|
57
|
+
fi;
|
|
58
|
+
if [ -n "${vars.topic}" ]; then
|
|
59
|
+
printf '%s\n' "${vars.topic}" > "${vars.work_dir}/brief.md";
|
|
60
|
+
else
|
|
61
|
+
if [ ! -f "${vars.input_file}" ]; then
|
|
62
|
+
echo "input file not found: ${vars.input_file}" >&2; exit 1;
|
|
64
63
|
fi;
|
|
64
|
+
cp "${vars.input_file}" "${vars.work_dir}/brief.md";
|
|
65
|
+
fi;
|
|
66
|
+
echo 0 > "${vars.work_dir}/.revise-count";
|
|
65
67
|
|
|
66
68
|
- id: understand
|
|
67
69
|
description: "Analyze brief and generate podcast briefing (agent.run)"
|
|
@@ -71,9 +73,11 @@ states:
|
|
|
71
73
|
role: scribe
|
|
72
74
|
agent: ${vars.agent}
|
|
73
75
|
input: |
|
|
76
|
+
Invoke the kk:audio-authoring skill: operation `briefing`, mode `solo`.
|
|
74
77
|
Read the source brief at ${vars.work_dir}/brief.md.
|
|
75
78
|
Write a concise podcast briefing at ${vars.work_dir}/1-briefing/briefing.md.
|
|
76
79
|
Identify core thesis, key listener takeaways, audience profile, target spoken duration (${vars.target_duration_min} minutes), tone, and what material from the brief to emphasize or cut.
|
|
80
|
+
Flags: --dir ${vars.work_dir} --duration ${vars.target_duration_min}
|
|
77
81
|
expectFile: ${vars.work_dir}/1-briefing/briefing.md
|
|
78
82
|
|
|
79
83
|
- id: outline-write
|
|
@@ -84,6 +88,7 @@ states:
|
|
|
84
88
|
role: scribe
|
|
85
89
|
agent: ${vars.agent}
|
|
86
90
|
input: |
|
|
91
|
+
Invoke the kk:audio-authoring skill: operation `outline`, mode `solo`.
|
|
87
92
|
Read the briefing at ${vars.work_dir}/1-briefing/briefing.md and brief at ${vars.work_dir}/brief.md.
|
|
88
93
|
Generate spoken podcast outline options in ${vars.work_dir}/2-outline/:
|
|
89
94
|
- outline-option-a.md (e.g. narrative / storytelling arc)
|
|
@@ -91,11 +96,12 @@ states:
|
|
|
91
96
|
- optional outline-option-c.md
|
|
92
97
|
Target total episode spoken duration is ${vars.target_duration_min} minutes.
|
|
93
98
|
Do not wait for operator selection; write the option files.
|
|
99
|
+
Flags: --dir ${vars.work_dir} --duration ${vars.target_duration_min}
|
|
94
100
|
expectFile: ${vars.work_dir}/2-outline/outline-option-a.md
|
|
95
101
|
- kind: shell
|
|
96
102
|
options:
|
|
97
|
-
|
|
98
|
-
|
|
103
|
+
command: >-
|
|
104
|
+
test -f "${vars.work_dir}/2-outline/outline-option-b.md" || { echo "missing required outline-option-b.md" >&2; exit 1; }
|
|
99
105
|
|
|
100
106
|
- id: outline-select
|
|
101
107
|
description: "Operator selects preferred outline option"
|
|
@@ -115,15 +121,15 @@ states:
|
|
|
115
121
|
onEnter:
|
|
116
122
|
- kind: shell
|
|
117
123
|
options:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
command: >-
|
|
125
|
+
sel="${vars.outline}";
|
|
126
|
+
if [ -z "$sel" ]; then sel="${vars.__hitlAnswer}"; fi;
|
|
127
|
+
if [ -z "$sel" ]; then sel="a"; fi;
|
|
128
|
+
src="${vars.work_dir}/2-outline/outline-option-$sel.md";
|
|
129
|
+
if [ ! -f "$src" ]; then
|
|
130
|
+
echo "selected outline file not found: $src" >&2; exit 1;
|
|
131
|
+
fi;
|
|
132
|
+
cp "$src" "${vars.work_dir}/2-outline/outline-approved.md";
|
|
127
133
|
|
|
128
134
|
- id: script-write
|
|
129
135
|
description: "Author complete VoiceScript YAML from approved outline (agent.run)"
|
|
@@ -133,6 +139,7 @@ states:
|
|
|
133
139
|
role: scribe
|
|
134
140
|
agent: ${vars.agent}
|
|
135
141
|
input: |
|
|
142
|
+
Invoke the kk:audio-authoring skill: operation `script`, mode `solo`.
|
|
136
143
|
Read the approved outline at ${vars.work_dir}/2-outline/outline-approved.md and briefing at ${vars.work_dir}/1-briefing/briefing.md.
|
|
137
144
|
Target duration is ${vars.target_duration_min} minutes.
|
|
138
145
|
Spoken budget: if language (${vars.language}) is zh, ja, or ko, target characters = ${vars.target_duration_min} * ${vars.cjk_chars_per_min}; otherwise target words = ${vars.target_duration_min} * ${vars.words_per_min}. Land inside ±20%, spoken prose, one speaker.
|
|
@@ -147,6 +154,8 @@ states:
|
|
|
147
154
|
gap_ms: 400 # optional, >= 0, silence before next clip
|
|
148
155
|
instruct: "warm" # optional, <= 500 characters
|
|
149
156
|
Do not invent fields. Revision feedback: ${vars.__hitlInput}
|
|
157
|
+
Duration retries: if ${vars.work_dir}/.duration-feedback exists, invoke with `--force` (authorized rewrite) and treat its contents as the revision feedback.
|
|
158
|
+
Flags: --dir ${vars.work_dir} --duration ${vars.target_duration_min} --language ${vars.language} --profile ${vars.voice_profile} --revise "${vars.__hitlInput}"
|
|
150
159
|
expectFile: ${vars.work_dir}/3-script/voicescript.yaml
|
|
151
160
|
|
|
152
161
|
- id: script-validate
|
|
@@ -154,18 +163,26 @@ states:
|
|
|
154
163
|
onEnter:
|
|
155
164
|
- kind: shell
|
|
156
165
|
options:
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
166
|
+
command: >-
|
|
167
|
+
set -e;
|
|
168
|
+
vs="${vars.validate_script}";
|
|
169
|
+
if [ ! -f "$vs" ]; then
|
|
170
|
+
kbin="$(command -v kk 2>/dev/null || true)";
|
|
171
|
+
while [ -L "$kbin" ]; do
|
|
172
|
+
link="$(readlink "$kbin")";
|
|
173
|
+
case "$link" in /*) kbin="$link";; *) kbin="$(dirname "$kbin")/$link";; esac;
|
|
174
|
+
done;
|
|
175
|
+
pkg=""; [ -n "$kbin" ] && pkg="$(cd "$(dirname "$kbin")/.." 2>/dev/null && pwd || true)";
|
|
176
|
+
vs="";
|
|
177
|
+
if [ -n "$KK_WORKFLOWS_DIR" ] && [ -f "$KK_WORKFLOWS_DIR/validate-voicescript.ts" ]; then vs="$KK_WORKFLOWS_DIR/validate-voicescript.ts";
|
|
178
|
+
elif [ -n "$pkg" ] && [ -f "$pkg/plugins/kk/workflows/validate-voicescript.ts" ]; then vs="$pkg/plugins/kk/workflows/validate-voicescript.ts";
|
|
179
|
+
elif [ -f "$HOME/.config/kk/workflows/validate-voicescript.ts" ]; then vs="$HOME/.config/kk/workflows/validate-voicescript.ts"; fi;
|
|
180
|
+
fi;
|
|
181
|
+
if [ -z "$vs" ]; then
|
|
182
|
+
echo "validate_script not found (searched: \$KK_WORKFLOWS_DIR, kk package root via \`command -v kk\`, \$HOME/.config/kk/workflows) — install the kk package or place an override with /workflow-run kk-solo-podcast --force" >&2;
|
|
183
|
+
exit 1;
|
|
184
|
+
fi;
|
|
185
|
+
bun "$vs" --in "${vars.work_dir}/3-script/voicescript.yaml"
|
|
169
186
|
|
|
170
187
|
- id: script-review
|
|
171
188
|
description: "Operator reviews and approves VoiceScript before live TTS generation"
|
|
@@ -182,13 +199,10 @@ states:
|
|
|
182
199
|
onEnter:
|
|
183
200
|
- kind: shell
|
|
184
201
|
options:
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
count_file=".spur/run/$run_id-solo-revise-count";
|
|
190
|
-
curr=$(cat "$count_file" 2>/dev/null || echo 0);
|
|
191
|
-
echo $((curr + 1)) > "$count_file";
|
|
202
|
+
command: >-
|
|
203
|
+
rm -f "${vars.work_dir}/.duration-feedback";
|
|
204
|
+
n=$(cat "${vars.work_dir}/.revise-count" 2>/dev/null || echo 0);
|
|
205
|
+
echo $((n + 1)) > "${vars.work_dir}/.revise-count";
|
|
192
206
|
- kind: hitl.input
|
|
193
207
|
options:
|
|
194
208
|
prompt: "Provide specific revision feedback for ${vars.work_dir}/3-script/voicescript.yaml:"
|
|
@@ -199,41 +213,65 @@ states:
|
|
|
199
213
|
onEnter:
|
|
200
214
|
- kind: shell
|
|
201
215
|
options:
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
' "${vars.work_dir}/3-script/voicescript.yaml" "${vars.work_dir}/4-audio/docs.json" "${vars.voice_profile}"
|
|
216
|
+
command: >-
|
|
217
|
+
set -e;
|
|
218
|
+
ws="${vars.wrap_script}";
|
|
219
|
+
if [ ! -f "$ws" ]; then
|
|
220
|
+
kbin="$(command -v kk 2>/dev/null || true)";
|
|
221
|
+
while [ -L "$kbin" ]; do
|
|
222
|
+
link="$(readlink "$kbin")";
|
|
223
|
+
case "$link" in /*) kbin="$link";; *) kbin="$(dirname "$kbin")/$link";; esac;
|
|
224
|
+
done;
|
|
225
|
+
pkg=""; [ -n "$kbin" ] && pkg="$(cd "$(dirname "$kbin")/.." 2>/dev/null && pwd || true)";
|
|
226
|
+
ws="";
|
|
227
|
+
if [ -n "$KK_WORKFLOWS_DIR" ] && [ -f "$KK_WORKFLOWS_DIR/wrap-voicescript-doc.ts" ]; then ws="$KK_WORKFLOWS_DIR/wrap-voicescript-doc.ts";
|
|
228
|
+
elif [ -n "$pkg" ] && [ -f "$pkg/plugins/kk/workflows/wrap-voicescript-doc.ts" ]; then ws="$pkg/plugins/kk/workflows/wrap-voicescript-doc.ts";
|
|
229
|
+
elif [ -f "$HOME/.config/kk/workflows/wrap-voicescript-doc.ts" ]; then ws="$HOME/.config/kk/workflows/wrap-voicescript-doc.ts"; fi;
|
|
230
|
+
fi;
|
|
231
|
+
if [ -z "$ws" ]; then
|
|
232
|
+
echo "wrap_script not found (searched: \$KK_WORKFLOWS_DIR, kk package root via \`command -v kk\`, \$HOME/.config/kk/workflows) — install the kk package or place an override with /workflow-run kk-solo-podcast --force" >&2;
|
|
233
|
+
exit 1;
|
|
234
|
+
fi;
|
|
235
|
+
bun "$ws" "${vars.work_dir}/3-script/voicescript.yaml" "${vars.work_dir}/4-audio/docs.json" "${vars.voice_profile}"
|
|
223
236
|
|
|
224
237
|
- id: generate
|
|
225
238
|
description: "Invoke voice-gen generator plugin to generate audio"
|
|
226
239
|
onEnter:
|
|
227
240
|
- kind: shell
|
|
228
241
|
options:
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
242
|
+
command: >-
|
|
243
|
+
set -e;
|
|
244
|
+
extra="";
|
|
245
|
+
if [ -n "${vars.plugins_path}" ]; then extra="--plugins-path ${vars.plugins_path}"; fi;
|
|
246
|
+
kk executor run voice-gen --in "${vars.work_dir}/4-audio/docs.json" --out "${vars.work_dir}/4-audio/content.json" $extra
|
|
234
247
|
|
|
235
248
|
- id: duration-check
|
|
236
|
-
description: "Validate generated audio duration against target budget"
|
|
249
|
+
description: "Validate generated audio duration against target budget; miss writes .duration-feedback for the retry edge"
|
|
250
|
+
onEnter:
|
|
251
|
+
- kind: shell
|
|
252
|
+
options:
|
|
253
|
+
command: >-
|
|
254
|
+
rm -f "${vars.work_dir}/4-audio/.duration-ok";
|
|
255
|
+
if [ ! -f "${vars.work_dir}/4-audio/content.json" ]; then
|
|
256
|
+
exit 0;
|
|
257
|
+
fi;
|
|
258
|
+
dur=$(jq -r '.metadata.duration // empty' "${vars.work_dir}/4-audio/content.json" 2>/dev/null);
|
|
259
|
+
if [ -z "$dur" ]; then
|
|
260
|
+
printf 'Regenerate: audio metadata.duration missing or not a number.\n' > "${vars.work_dir}/.duration-feedback";
|
|
261
|
+
n=$(cat "${vars.work_dir}/.revise-count" 2>/dev/null || echo 0);
|
|
262
|
+
echo $((n+1)) > "${vars.work_dir}/.revise-count";
|
|
263
|
+
exit 0;
|
|
264
|
+
fi;
|
|
265
|
+
target_s=$((${vars.target_duration_min} * 60));
|
|
266
|
+
min_s=$(awk -v t="$target_s" 'BEGIN {print t * 0.5}');
|
|
267
|
+
max_s=$(awk -v t="$target_s" 'BEGIN {print t * 2.0}');
|
|
268
|
+
if awk -v d="$dur" -v min="$min_s" -v max="$max_s" 'BEGIN { exit !(d >= min && d <= max) }'; then
|
|
269
|
+
touch "${vars.work_dir}/4-audio/.duration-ok";
|
|
270
|
+
else
|
|
271
|
+
printf 'Regenerate: last audio duration %ss outside 0.5x–2x of %ss; adjust segment lengths toward the target.\n' "$dur" "$target_s" > "${vars.work_dir}/.duration-feedback";
|
|
272
|
+
n=$(cat "${vars.work_dir}/.revise-count" 2>/dev/null || echo 0);
|
|
273
|
+
echo $((n+1)) > "${vars.work_dir}/.revise-count";
|
|
274
|
+
fi
|
|
237
275
|
|
|
238
276
|
- id: done
|
|
239
277
|
description: "Terminal — Solo podcast VoiceScript and audio generated successfully"
|
|
@@ -314,7 +352,7 @@ transitions:
|
|
|
314
352
|
guard:
|
|
315
353
|
kind: shell
|
|
316
354
|
options:
|
|
317
|
-
command: 'test "${vars.__hitlAnswer}" = "no" &&
|
|
355
|
+
command: 'test "${vars.__hitlAnswer}" = "no" && test "$(cat "${vars.work_dir}/.revise-count" 2>/dev/null || echo 0)" -lt 3'
|
|
318
356
|
|
|
319
357
|
- from: script-review
|
|
320
358
|
to: failed
|
|
@@ -322,7 +360,7 @@ transitions:
|
|
|
322
360
|
guard:
|
|
323
361
|
kind: shell
|
|
324
362
|
options:
|
|
325
|
-
command: 'test "${vars.__hitlAnswer}" = "no" &&
|
|
363
|
+
command: 'test "${vars.__hitlAnswer}" = "no" && test "$(cat "${vars.work_dir}/.revise-count" 2>/dev/null || echo 0)" -ge 3'
|
|
326
364
|
|
|
327
365
|
- from: script-revise
|
|
328
366
|
to: script-write
|
|
@@ -348,27 +386,20 @@ transitions:
|
|
|
348
386
|
guard:
|
|
349
387
|
kind: shell
|
|
350
388
|
options:
|
|
351
|
-
command:
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
389
|
+
command: 'test ! -f "${vars.work_dir}/4-audio/content.json" || test -f "${vars.work_dir}/4-audio/.duration-ok"'
|
|
390
|
+
|
|
391
|
+
- from: duration-check
|
|
392
|
+
to: script-write
|
|
393
|
+
description: "Audio duration outside band with revision budget remaining -> regenerate script from feedback"
|
|
394
|
+
guard:
|
|
395
|
+
kind: shell
|
|
396
|
+
options:
|
|
397
|
+
command: 'test -f "${vars.work_dir}/4-audio/content.json" && test ! -f "${vars.work_dir}/4-audio/.duration-ok" && test "$(cat "${vars.work_dir}/.revise-count" 2>/dev/null || echo 0)" -lt 3'
|
|
356
398
|
|
|
357
399
|
- from: duration-check
|
|
358
400
|
to: failed
|
|
359
|
-
description: "Audio duration
|
|
401
|
+
description: "Audio duration outside band with revision budget exhausted -> fail"
|
|
360
402
|
guard:
|
|
361
403
|
kind: shell
|
|
362
404
|
options:
|
|
363
|
-
command:
|
|
364
|
-
test -f "${vars.work_dir}/4-audio/content.json" && {
|
|
365
|
-
dur=$(jq -r '.metadata.duration // empty' "${vars.work_dir}/4-audio/content.json" 2>/dev/null);
|
|
366
|
-
if [ -z "$dur" ]; then
|
|
367
|
-
echo "duration-check failed: metadata.duration missing or not a number" >&2;
|
|
368
|
-
exit 0;
|
|
369
|
-
fi;
|
|
370
|
-
target_s=$((${vars.target_duration_min} * 60));
|
|
371
|
-
min_s=$(awk -v t="$target_s" 'BEGIN {print t * 0.5}');
|
|
372
|
-
max_s=$(awk -v t="$target_s" 'BEGIN {print t * 2.0}');
|
|
373
|
-
awk -v d="$dur" -v min="$min_s" -v max="$max_s" -v ts="$target_s" 'BEGIN { if (d < min || d > max) { printf "duration-check failed: duration %ss outside 0.5x–2x of %ss\n", d, ts > "/dev/stderr"; exit 0 } else exit 1 }';
|
|
374
|
-
}
|
|
405
|
+
command: 'test -f "${vars.work_dir}/4-audio/content.json" && test ! -f "${vars.work_dir}/4-audio/.duration-ok" && test "$(cat "${vars.work_dir}/.revise-count" 2>/dev/null || echo 0)" -ge 3'
|
|
@@ -37,10 +37,10 @@ vars:
|
|
|
37
37
|
maxResults: "8" # written into topic.json as int (0054 maxResults)
|
|
38
38
|
fixture: "false" # "true" selects the stub; CI/dry-run must pass "true"
|
|
39
39
|
work_dir: "./work" # RESOLVED topic dir ($works_root/<topic-id>)
|
|
40
|
-
plugins_path: "
|
|
40
|
+
plugins_path: "" # empty -> ADR-012 default discovery (package-shipped built-ins); set for checkout dev
|
|
41
41
|
ingestion_plugin: "web-search"
|
|
42
42
|
generator_plugin: "content-gen"
|
|
43
|
-
render_script: "plugins/kk/scripts/render-md.ts
|
|
43
|
+
render_script: "" # empty -> fallback chain: kk package root -> checkout plugins/kk/scripts/render-md.ts
|
|
44
44
|
|
|
45
45
|
states:
|
|
46
46
|
- id: prepare
|
|
@@ -103,9 +103,11 @@ states:
|
|
|
103
103
|
- kind: shell
|
|
104
104
|
options:
|
|
105
105
|
command: >-
|
|
106
|
+
set --;
|
|
107
|
+
if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
|
|
106
108
|
kk executor run "${vars.ingestion_plugin}" --in
|
|
107
109
|
"${vars.work_dir}/topic.json" --out "${vars.work_dir}/docs.json"
|
|
108
|
-
|
|
110
|
+
"$@"
|
|
109
111
|
|
|
110
112
|
- id: generate
|
|
111
113
|
description: >-
|
|
@@ -123,9 +125,11 @@ states:
|
|
|
123
125
|
- kind: shell
|
|
124
126
|
options:
|
|
125
127
|
command: >-
|
|
128
|
+
set --;
|
|
129
|
+
if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
|
|
126
130
|
kk executor run "${vars.generator_plugin}" --in
|
|
127
131
|
"${vars.work_dir}/docs.json" --out "${vars.work_dir}/content.json"
|
|
128
|
-
|
|
132
|
+
"$@"
|
|
129
133
|
|
|
130
134
|
- id: render
|
|
131
135
|
description: >-
|
|
@@ -135,7 +139,24 @@ states:
|
|
|
135
139
|
- kind: shell
|
|
136
140
|
options:
|
|
137
141
|
command: >-
|
|
138
|
-
|
|
142
|
+
set -e;
|
|
143
|
+
rs="${vars.render_script}";
|
|
144
|
+
if [ ! -f "$rs" ]; then
|
|
145
|
+
kbin="$(command -v kk 2>/dev/null || true)";
|
|
146
|
+
while [ -L "$kbin" ]; do
|
|
147
|
+
link="$(readlink "$kbin")";
|
|
148
|
+
case "$link" in /*) kbin="$link";; *) kbin="$(dirname "$kbin")/$link";; esac;
|
|
149
|
+
done;
|
|
150
|
+
pkg=""; [ -n "$kbin" ] && pkg="$(cd "$(dirname "$kbin")/.." 2>/dev/null && pwd || true)";
|
|
151
|
+
rs="";
|
|
152
|
+
if [ -n "$pkg" ] && [ -f "$pkg/plugins/kk/scripts/render-md.ts" ]; then rs="$pkg/plugins/kk/scripts/render-md.ts";
|
|
153
|
+
elif [ -f "plugins/kk/scripts/render-md.ts" ]; then rs="plugins/kk/scripts/render-md.ts"; fi;
|
|
154
|
+
fi;
|
|
155
|
+
if [ -z "$rs" ]; then
|
|
156
|
+
echo "render_script not found (searched: var, kk package root via \`command -v kk\`, ./plugins/kk/scripts)" >&2;
|
|
157
|
+
exit 1;
|
|
158
|
+
fi;
|
|
159
|
+
bun "$rs" --in "${vars.work_dir}/content.json"
|
|
139
160
|
--out "${vars.work_dir}/content.md"
|
|
140
161
|
|
|
141
162
|
- id: done
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* Runtime VoiceScript→Doc[] wrapper for kk-solo-podcast.
|
|
4
|
+
*
|
|
5
|
+
* Copied with the workflow YAML into $KK_WORKFLOWS_DIR (default
|
|
6
|
+
* ~/.config/kk/workflows). Node/Bun builtins only — no @gobing-ai/* and no
|
|
7
|
+
* relative imports (those paths do not exist next to the runtime copy).
|
|
8
|
+
*
|
|
9
|
+
* SYNC: plugins/kk/workflows/kk-solo-podcast.yaml (wrap-docs state shells this
|
|
10
|
+
* script with scriptPath, outPath, voiceProfile argv). The wrap shape must stay
|
|
11
|
+
* a single-Doc array with sha256(file://scriptPath)[0:16] id — voice-gen and
|
|
12
|
+
* tests depend on it.
|
|
13
|
+
*/
|
|
14
|
+
import { createHash } from 'node:crypto';
|
|
15
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
16
|
+
import { resolve } from 'node:path';
|
|
17
|
+
|
|
18
|
+
const [scriptPathArg, outPathArg, voiceProfileArg] = process.argv.slice(2);
|
|
19
|
+
|
|
20
|
+
if (!scriptPathArg || !outPathArg) {
|
|
21
|
+
console.error('usage: wrap-voicescript-doc.ts <scriptPath> <outPath> [voiceProfile]');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const scriptPath = resolve(scriptPathArg);
|
|
26
|
+
const outPath = resolve(outPathArg);
|
|
27
|
+
const voiceProfile = voiceProfileArg || process.env.VOICEBOX_DEFAULT_PROFILE || '';
|
|
28
|
+
const body = readFileSync(scriptPath, 'utf-8');
|
|
29
|
+
const sourceUri = `file://${scriptPath}`;
|
|
30
|
+
const id = createHash('sha256').update(sourceUri).digest('hex').slice(0, 16);
|
|
31
|
+
const doc = {
|
|
32
|
+
id,
|
|
33
|
+
title: 'Podcast VoiceScript',
|
|
34
|
+
sourceUri,
|
|
35
|
+
body,
|
|
36
|
+
mediaType: 'application/yaml',
|
|
37
|
+
metadata: voiceProfile ? { voiceProfile } : {},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
writeFileSync(outPath, JSON.stringify([doc], null, 2));
|