@gobing-ai/knowledge-kit 0.0.36 → 0.1.0

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 (159) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/dist/board/collect/index.css +654 -16
  3. package/dist/board/collect/index.js +191 -64
  4. package/dist/board/daily-voice/index.css +679 -12
  5. package/dist/board/daily-voice/index.js +185 -66
  6. package/dist/board/itc/index.css +679 -13
  7. package/dist/board/itc/index.js +188 -66
  8. package/dist/index.js +2878 -152
  9. package/package.json +3 -2
  10. package/plugins/generations/content-gen/dist/index.js +158 -12
  11. package/plugins/generations/core-facts-gen/dist/index.js +158 -12
  12. package/plugins/generations/daily-article-gen/dist/index.js +158 -12
  13. package/plugins/generations/dailynews-gen/dist/index.js +180 -12
  14. package/plugins/generations/dailynews-gen/src/script-plan.ts +32 -0
  15. package/plugins/generations/episode-plan-gen/dist/index.js +158 -12
  16. package/plugins/generations/gemini-tts/README.md +4 -3
  17. package/plugins/generations/gemini-tts/dist/index.js +158 -12
  18. package/plugins/generations/image-gen/dist/index.js +158 -12
  19. package/plugins/generations/news-report-gen/dist/index.js +158 -12
  20. package/plugins/generations/omni-voice-gen/README.md +15 -4
  21. package/plugins/generations/omni-voice-gen/profiles.json +1 -2
  22. package/plugins/generations/voice-gen/dist/index.js +158 -12
  23. package/plugins/generations/voxcpm-tts/README.md +21 -1
  24. package/plugins/generations/voxcpm-tts/dist/index.js +173 -13
  25. package/plugins/generations/voxcpm-tts/scripts/audit-speaker-consistency.py +109 -0
  26. package/plugins/generations/voxcpm-tts/src/voxcpm-client.ts +28 -1
  27. package/plugins/ingestions/aihot-ingest/dist/index.js +158 -12
  28. package/plugins/ingestions/horizon-ingest/dist/index.js +158 -12
  29. package/plugins/ingestions/last30days-ingest/dist/index.js +158 -12
  30. package/plugins/ingestions/web-search/dist/index.js +158 -12
  31. package/plugins/kk/commands/workflow-run.md +56 -18
  32. package/plugins/kk/decisions/kk-itc.yaml +46 -0
  33. package/plugins/kk/decisions/kk-seo-keyword.yaml +57 -0
  34. package/plugins/kk/decisions/kk-seo-page.yaml +230 -0
  35. package/plugins/kk/decisions/kk-seo-pair.yaml +21 -0
  36. package/plugins/kk/decisions/kk-seo-site.yaml +90 -0
  37. package/plugins/kk/plugin.json +1 -1
  38. package/plugins/kk/publish-presets/chinese-article.json +14 -0
  39. package/plugins/kk/publish-presets/technical-article.json +14 -0
  40. package/plugins/kk/references/smart-seo-attribution.md +85 -0
  41. package/plugins/kk/scripts/daily-stages.ts +42 -2
  42. package/plugins/kk/scripts/daily-voice-style.zh.md +1 -1
  43. package/plugins/kk/scripts/itc-stages.ts +25 -118
  44. package/plugins/kk/scripts/kk-workflow-stages.ts +38 -1
  45. package/plugins/kk/scripts/seo-decisions.ts +477 -0
  46. package/plugins/kk/scripts/seo-enrich.ts +728 -0
  47. package/plugins/kk/scripts/seo-facts.ts +610 -0
  48. package/plugins/kk/scripts/seo-fetch.ts +766 -0
  49. package/plugins/kk/scripts/seo-report.ts +460 -0
  50. package/plugins/kk/scripts/seo-rules.ts +1882 -0
  51. package/plugins/kk/scripts/seo-stages.ts +1044 -0
  52. package/plugins/kk/skills/article-adapt/SKILL.md +2 -2
  53. package/plugins/kk/skills/smart-seo/SKILL.md +183 -0
  54. package/plugins/kk/skills/smart-seo/references/method.md +126 -0
  55. package/plugins/kk/skills/smart-seo/references/narrative.md +125 -0
  56. package/plugins/kk/skills/smart-seo/references/operations.md +202 -0
  57. package/plugins/kk/skills/smart-seo/references/seo-geo-sources.md +90 -0
  58. package/plugins/kk/workflows/feature-verification.yaml +28 -24
  59. package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +86 -18
  60. package/plugins/kk/workflows/kk-itc-author.yaml +406 -0
  61. package/plugins/kk/workflows/kk-itc.yaml +208 -682
  62. package/plugins/kk/workflows/kk-smart-seo.yaml +251 -0
  63. package/plugins/publishings/bilibili-pub/dist/index.js +21782 -0
  64. package/plugins/publishings/bilibili-pub/dist/profile-check.js +20410 -0
  65. package/plugins/publishings/bilibili-pub/package.json +20 -0
  66. package/plugins/publishings/bilibili-pub/plugin.json +12 -0
  67. package/plugins/publishings/bilibili-pub/src/flow.ts +1296 -0
  68. package/plugins/publishings/bilibili-pub/src/index.ts +139 -0
  69. package/plugins/publishings/bilibili-pub/src/profile-check.ts +19 -0
  70. package/plugins/publishings/bilibili-pub/src/selectors.ts +222 -0
  71. package/plugins/publishings/bilibili-pub/tsconfig.json +4 -0
  72. package/plugins/publishings/cnblogs-pub/dist/index.js +21383 -0
  73. package/plugins/publishings/cnblogs-pub/dist/profile-check.js +20412 -0
  74. package/plugins/publishings/cnblogs-pub/package.json +19 -0
  75. package/plugins/publishings/cnblogs-pub/plugin.json +12 -0
  76. package/plugins/publishings/cnblogs-pub/src/flow.ts +708 -0
  77. package/plugins/publishings/cnblogs-pub/src/index.ts +172 -0
  78. package/plugins/publishings/cnblogs-pub/src/profile-check.ts +24 -0
  79. package/plugins/publishings/cnblogs-pub/src/selectors.ts +104 -0
  80. package/plugins/publishings/cnblogs-pub/tsconfig.json +4 -0
  81. package/plugins/publishings/csdn-pub/dist/index.js +21552 -0
  82. package/plugins/publishings/csdn-pub/dist/profile-check.js +20963 -0
  83. package/plugins/publishings/csdn-pub/package.json +19 -0
  84. package/plugins/publishings/csdn-pub/plugin.json +12 -0
  85. package/plugins/publishings/csdn-pub/src/index.ts +1178 -0
  86. package/plugins/publishings/csdn-pub/src/profile-check.ts +23 -0
  87. package/plugins/publishings/csdn-pub/tsconfig.json +4 -0
  88. package/plugins/publishings/emdash-pub/dist/index.js +158 -12
  89. package/plugins/publishings/emdash-pub/plugin.json +22 -1
  90. package/plugins/publishings/infoq-pub/dist/index.js +160 -12
  91. package/plugins/publishings/infoq-pub/dist/profile-check.js +160 -12
  92. package/plugins/publishings/infoq-pub/plugin.json +21 -0
  93. package/plugins/publishings/jike-pub/dist/index.js +21379 -0
  94. package/plugins/publishings/jike-pub/dist/profile-check.js +20951 -0
  95. package/plugins/publishings/jike-pub/package.json +19 -0
  96. package/plugins/publishings/jike-pub/plugin.json +12 -0
  97. package/plugins/publishings/jike-pub/src/index.ts +800 -0
  98. package/plugins/publishings/jike-pub/src/profile-check.ts +21 -0
  99. package/plugins/publishings/jike-pub/tsconfig.json +4 -0
  100. package/plugins/publishings/juejin-pub/dist/index.js +160 -12
  101. package/plugins/publishings/juejin-pub/dist/profile-check.js +160 -12
  102. package/plugins/publishings/juejin-pub/plugin.json +21 -0
  103. package/plugins/publishings/medium-pub/dist/index.js +158 -12
  104. package/plugins/publishings/medium-pub/plugin.json +22 -1
  105. package/plugins/publishings/podcast-pub/dist/index.js +158 -12
  106. package/plugins/publishings/qiita-pub/dist/index.js +158 -12
  107. package/plugins/publishings/qiita-pub/plugin.json +24 -1
  108. package/plugins/publishings/shipinhao-pub/dist/index.js +21715 -0
  109. package/plugins/publishings/shipinhao-pub/dist/profile-check.js +20431 -0
  110. package/plugins/publishings/shipinhao-pub/package.json +19 -0
  111. package/plugins/publishings/shipinhao-pub/plugin.json +12 -0
  112. package/plugins/publishings/shipinhao-pub/src/flow.ts +1323 -0
  113. package/plugins/publishings/shipinhao-pub/src/index.ts +173 -0
  114. package/plugins/publishings/shipinhao-pub/src/profile-check.ts +25 -0
  115. package/plugins/publishings/shipinhao-pub/src/selectors.ts +241 -0
  116. package/plugins/publishings/shipinhao-pub/tsconfig.json +4 -0
  117. package/plugins/publishings/sspai-pub/dist/index.js +21371 -0
  118. package/plugins/publishings/sspai-pub/dist/profile-check.js +20414 -0
  119. package/plugins/publishings/sspai-pub/package.json +19 -0
  120. package/plugins/publishings/sspai-pub/plugin.json +12 -0
  121. package/plugins/publishings/sspai-pub/src/flow.ts +624 -0
  122. package/plugins/publishings/sspai-pub/src/index.ts +167 -0
  123. package/plugins/publishings/sspai-pub/src/profile-check.ts +25 -0
  124. package/plugins/publishings/sspai-pub/src/selectors.ts +122 -0
  125. package/plugins/publishings/sspai-pub/tsconfig.json +4 -0
  126. package/plugins/publishings/substack-pub/dist/index.js +160 -12
  127. package/plugins/publishings/substack-pub/dist/profile-check.js +160 -12
  128. package/plugins/publishings/substack-pub/plugin.json +21 -0
  129. package/plugins/publishings/surfdash-pub/dist/index.js +158 -12
  130. package/plugins/publishings/surfdash-pub/plugin.json +22 -1
  131. package/plugins/publishings/threads-pub/dist/index.js +158 -12
  132. package/plugins/publishings/wechatmp-pub/dist/index.js +1669 -502
  133. package/plugins/publishings/wechatmp-pub/dist/profile-check.js +204 -21
  134. package/plugins/publishings/wechatmp-pub/plugin.json +32 -0
  135. package/plugins/publishings/wechatmp-pub/src/cgi-draft.ts +540 -0
  136. package/plugins/publishings/wechatmp-pub/src/cgi-media.ts +380 -0
  137. package/plugins/publishings/wechatmp-pub/src/cgi.ts +485 -0
  138. package/plugins/publishings/wechatmp-pub/src/flow.ts +104 -0
  139. package/plugins/publishings/weibo-pub/dist/index.js +160 -12
  140. package/plugins/publishings/weibo-pub/dist/profile-check.js +160 -12
  141. package/plugins/publishings/weibo-pub/plugin.json +35 -0
  142. package/plugins/publishings/x-pub/dist/index.js +160 -12
  143. package/plugins/publishings/x-pub/dist/profile-check.js +160 -12
  144. package/plugins/publishings/x-pub/plugin.json +40 -0
  145. package/plugins/publishings/xhs-pub/dist/index.js +160 -12
  146. package/plugins/publishings/xhs-pub/dist/profile-check.js +160 -12
  147. package/plugins/publishings/xhs-pub/plugin.json +37 -0
  148. package/plugins/publishings/xiaoyuzhou-pub/dist/index.js +21491 -0
  149. package/plugins/publishings/xiaoyuzhou-pub/dist/profile-check.js +20417 -0
  150. package/plugins/publishings/xiaoyuzhou-pub/package.json +19 -0
  151. package/plugins/publishings/xiaoyuzhou-pub/plugin.json +12 -0
  152. package/plugins/publishings/xiaoyuzhou-pub/src/flow.ts +826 -0
  153. package/plugins/publishings/xiaoyuzhou-pub/src/index.ts +174 -0
  154. package/plugins/publishings/xiaoyuzhou-pub/src/profile-check.ts +26 -0
  155. package/plugins/publishings/xiaoyuzhou-pub/src/selectors.ts +125 -0
  156. package/plugins/publishings/xiaoyuzhou-pub/tsconfig.json +4 -0
  157. package/plugins/publishings/zenn-pub/dist/index.js +158 -12
  158. package/plugins/publishings/zenn-pub/plugin.json +22 -1
  159. package/plugins/generations/omni-voice-gen/voices/robin-news.wav +0 -0
@@ -12005,6 +12005,83 @@ import { createHash } from "crypto";
12005
12005
  import { dirname as dirname2, join } from "path";
12006
12006
  import { parseArgs } from "util";
12007
12007
 
12008
+ // ../../packages/kk-core/src/content-limits.ts
12009
+ var PLATFORM_CONTENT_LIMITS = Object.freeze({
12010
+ schemaVersion: 5,
12011
+ tagLimits: Object.freeze({
12012
+ default: 7,
12013
+ imageText: Object.freeze({
12014
+ instagram: 5,
12015
+ douyin: 5,
12016
+ kuaishou: 4,
12017
+ weixin: 1,
12018
+ xiaohongshu: 10
12019
+ }),
12020
+ shortVideo: Object.freeze({
12021
+ douyin: 5,
12022
+ kuaishou: 4,
12023
+ xiaohongshu: 10,
12024
+ bilibili: 10,
12025
+ zhihu: 5,
12026
+ tiktok: 20,
12027
+ youtube: 60
12028
+ })
12029
+ }),
12030
+ titleCapabilities: Object.freeze({
12031
+ dynamic: Object.freeze({
12032
+ bilibili: Object.freeze({ max: 20, unit: "utf16_code_units" }),
12033
+ zhihu: Object.freeze({ max: 50, unit: "utf16_code_units" }),
12034
+ douban: Object.freeze({ max: 70, unit: "utf16_code_units" }),
12035
+ x: null,
12036
+ threads: null,
12037
+ weibo: null,
12038
+ jike: null,
12039
+ zsxq: null,
12040
+ toutiao: null,
12041
+ juejin: null
12042
+ }),
12043
+ imageText: Object.freeze({
12044
+ xiaohongshu: Object.freeze({ max: 20, unit: "xiaohongshu_title_weighted" }),
12045
+ weixin: Object.freeze({ max: 20, unit: "utf16_code_units" }),
12046
+ douyin: Object.freeze({ max: 20, unit: "utf16_code_units" }),
12047
+ shipinhao: Object.freeze({ max: 22, unit: "utf16_code_units" }),
12048
+ reddit: Object.freeze({ max: 300, unit: "utf16_code_units" })
12049
+ }),
12050
+ article: Object.freeze({
12051
+ juejin: Object.freeze({ max: 100, unit: "utf16_code_units" }),
12052
+ csdn: Object.freeze({ max: 100, unit: "utf16_code_units" }),
12053
+ cnblogs: Object.freeze({ max: 128, unit: "utf16_code_units" }),
12054
+ weibo: Object.freeze({ max: 30, unit: "utf16_code_units" }),
12055
+ sspai: Object.freeze({ max: 80, unit: "utf16_code_units" })
12056
+ }),
12057
+ shortVideo: Object.freeze({
12058
+ shipinhao: Object.freeze({ max: 16, unit: "utf16_code_units" }),
12059
+ douyin: Object.freeze({ max: 30, unit: "utf16_code_units" }),
12060
+ bilibili: Object.freeze({ max: 80, unit: "utf16_code_units" })
12061
+ })
12062
+ }),
12063
+ bodyCapabilities: Object.freeze({
12064
+ dynamic: Object.freeze({
12065
+ threads: Object.freeze({ max: 500, unit: "unicode_code_points", behavior: "split_thread" }),
12066
+ x: Object.freeze({ max: 280, unit: "weighted", behavior: "split_thread" }),
12067
+ weibo: Object.freeze({ max: 2000, unit: "unicode_code_points" }),
12068
+ jike: Object.freeze({ max: 1000, unit: "unicode_code_points" })
12069
+ }),
12070
+ imageText: Object.freeze({
12071
+ xiaohongshu: Object.freeze({ max: 1000, unit: "unicode_code_points" }),
12072
+ douyin: Object.freeze({ max: 1000, unit: "unicode_code_points" })
12073
+ }),
12074
+ article: Object.freeze({
12075
+ wechatmp: Object.freeze({ max: 50000, unit: "unicode_code_points" }),
12076
+ juejin: Object.freeze({ max: 50000, unit: "unicode_code_points" })
12077
+ }),
12078
+ shortVideo: Object.freeze({
12079
+ douyin: Object.freeze({ max: 1000, unit: "unicode_code_points" }),
12080
+ kuaishou: Object.freeze({ max: 1000, unit: "unicode_code_points" }),
12081
+ shipinhao: Object.freeze({ max: 1000, unit: "unicode_code_points" })
12082
+ })
12083
+ })
12084
+ });
12008
12085
  // ../../packages/kk-core/src/kinds.ts
12009
12086
  init_zod();
12010
12087
 
@@ -12095,6 +12172,67 @@ var BrowserSessionDescriptorSchema = object({
12095
12172
  }
12096
12173
  }).optional()
12097
12174
  }).strict();
12175
+ var ContentCountUnitSchema = _enum([
12176
+ "utf16_code_units",
12177
+ "unicode_code_points",
12178
+ "weighted",
12179
+ "xiaohongshu_title_weighted"
12180
+ ]);
12181
+ var LimitEntrySchema = object({
12182
+ max: number2().int().positive(),
12183
+ unit: ContentCountUnitSchema
12184
+ }).strict();
12185
+ var PublishingProfileLimitsSchema = object({
12186
+ title: LimitEntrySchema.optional(),
12187
+ body: LimitEntrySchema.optional()
12188
+ }).strict();
12189
+ var PublishingProfileRequiresSchema = object({
12190
+ cover: boolean2().optional(),
12191
+ orderedImages: boolean2().optional()
12192
+ }).strict();
12193
+ var PublishingProfileModeBindingSchema = object({
12194
+ metadata: record(string2(), unknown())
12195
+ }).strict();
12196
+ var PublishingProfileModesSchema = object({
12197
+ draft: PublishingProfileModeBindingSchema.optional(),
12198
+ live: PublishingProfileModeBindingSchema.optional()
12199
+ }).strict().superRefine((modes, ctx) => {
12200
+ if (modes.draft === undefined && modes.live === undefined) {
12201
+ ctx.addIssue({
12202
+ code: "custom",
12203
+ message: 'modes must define at least one of "draft" or "live"'
12204
+ });
12205
+ }
12206
+ });
12207
+ var PublishingProfileSchema = object({
12208
+ format: string2().min(1),
12209
+ locale: string2().regex(/^(source|[a-z]{2,3}(-[A-Za-z0-9]+)*)$/, {
12210
+ message: 'locale must be "source" or a valid language tag'
12211
+ }),
12212
+ limits: PublishingProfileLimitsSchema.optional(),
12213
+ requires: PublishingProfileRequiresSchema.optional(),
12214
+ modes: PublishingProfileModesSchema
12215
+ }).strict();
12216
+ var PublishingCapabilitiesSchema = object({
12217
+ defaultProfile: string2().min(1),
12218
+ profiles: record(string2().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/), PublishingProfileSchema)
12219
+ }).strict().superRefine((data, ctx) => {
12220
+ const keys = Object.keys(data.profiles);
12221
+ if (keys.length === 0) {
12222
+ ctx.addIssue({
12223
+ code: "custom",
12224
+ path: ["profiles"],
12225
+ message: "profiles record must be nonempty"
12226
+ });
12227
+ }
12228
+ if (!(data.defaultProfile in data.profiles)) {
12229
+ ctx.addIssue({
12230
+ code: "custom",
12231
+ path: ["defaultProfile"],
12232
+ message: `defaultProfile "${data.defaultProfile}" must reference a declared profile`
12233
+ });
12234
+ }
12235
+ });
12098
12236
  var PluginManifestSchemaBase = looseObject({
12099
12237
  name: string2().min(1),
12100
12238
  kind: PluginKindSchema,
@@ -12103,23 +12241,31 @@ var PluginManifestSchemaBase = looseObject({
12103
12241
  version: string2().optional(),
12104
12242
  description: string2().optional(),
12105
12243
  operations: record(string2(), PluginOperationSchema).optional(),
12106
- browserSession: BrowserSessionDescriptorSchema.optional()
12244
+ browserSession: BrowserSessionDescriptorSchema.optional(),
12245
+ publishing: PublishingCapabilitiesSchema.optional()
12107
12246
  });
12108
12247
  var PluginManifestSchema = PluginManifestSchemaBase.superRefine((manifest, ctx) => {
12109
- if (manifest.browserSession === undefined)
12110
- return;
12111
- if (manifest.kind !== "publish") {
12112
- ctx.addIssue({
12113
- code: "custom",
12114
- path: ["browserSession"],
12115
- message: `browserSession requires kind "publish", not "${manifest.kind}"`
12116
- });
12248
+ if (manifest.browserSession !== undefined) {
12249
+ if (manifest.kind !== "publish") {
12250
+ ctx.addIssue({
12251
+ code: "custom",
12252
+ path: ["browserSession"],
12253
+ message: `browserSession requires kind "publish", not "${manifest.kind}"`
12254
+ });
12255
+ }
12256
+ if (manifest.check === undefined) {
12257
+ ctx.addIssue({
12258
+ code: "custom",
12259
+ path: ["browserSession"],
12260
+ message: "browserSession requires a check probe entry that owns the session routines"
12261
+ });
12262
+ }
12117
12263
  }
12118
- if (manifest.check === undefined) {
12264
+ if (manifest.publishing !== undefined && manifest.kind !== "publish") {
12119
12265
  ctx.addIssue({
12120
12266
  code: "custom",
12121
- path: ["browserSession"],
12122
- message: "browserSession requires a check probe entry that owns the session routines"
12267
+ path: ["publishing"],
12268
+ message: `publishing requires kind "publish", not "${manifest.kind}"`
12123
12269
  });
12124
12270
  }
12125
12271
  });
@@ -33,12 +33,14 @@ Parse per the frozen rule:
33
33
  - `--in <file>` → file mode. `--force` → overwrite an existing dest YAML / existing `brief.md`.
34
34
  - Profile `kk-storm-research`: `--dir <path>` (default `$works_dir/kk-storm-research/<topic-id>`),
35
35
  `--fixture` → force `fixture: "true"`.
36
- - Profile `kk-itc`: `--dir <path>` (default `$works_dir/kk-itc/<kebab>`), `--playbook generic|english|wechat` (default `generic`),
37
- `--research` (`true`|`false`, default `false`), `--judge` (`true`|`false`, default `false`),
38
- `--outline <a|b|c>` (default empty), `--writer itc-generating|topic` (default `itc-generating`),
39
- `--auto`, `--unslop true|false` (default `false`), `--max-revisions <N>` (default `2`),
40
- `--cover`, `--inline-images`, `--targets <a,b>` (default empty), `--live`,
41
- `--source-locale en|zh|ja` (default `en`; `--playbook wechat` without it implies `zh`).
36
+ - Profile `kk-itc`: `--dir <path>` (default `$works_dir/kk-itc/<kebab>[/<run-date>]`),
37
+ `--from-collect <topic-profile>` (a third source, XOR with `<topic>` / `--in`),
38
+ `--topic-profile <slug>`, `--run-date <YYYYMMDD>`, `--playbook generic|english|wechat`,
39
+ `--research`, `--judge`, `--outline <a|b|c>`, `--writer itc-generating|topic`, `--auto`,
40
+ `--unslop true|false`, `--max-revisions <N>`, `--cover`, `--inline-images`, `--targets <a,b>`,
41
+ `--publish-preset <name>`, `--seo off|handoff`, `--live`, `--source-locale en|zh|ja`. Unpassed
42
+ flags take `workflows.kk-itc` config, then compiled defaults (`generic`, `itc-generating`, `en`,
43
+ `seo: off`, max revisions `2`, booleans `false`) — resolved once by `itc-prepare` (§4).
42
44
  `--fixture` passed with `kk-itc` or `kk-solo-podcast` → exit 1 (`fixture is storm-only`).
43
45
  - Profile `kk-solo-podcast`: `--dir <path>` (default `$works_dir/kk-solo-podcast/<date>`), `--outline <a|b|c>`
44
46
  (default empty), `--duration <min>` (default `8`), `--language <code>` (default `en`),
@@ -185,18 +187,51 @@ mkdir -p "$work_dir"
185
187
 
186
188
  ### Profile `kk-itc` (authoring workspace)
187
189
 
188
- `raw` = `TOPIC` or file-mode first ATX H1 else basename without extension.
189
- `kebab` = lower-case, `[^a-z0-9]+` → `-`, trimmed dashes (no length cap, no sha256 hash).
190
- `work_dir="${DIR:-$works_dir/kk-itc/$kebab}"`. Empty derived `kebab` → exit 1. An explicit
191
- `--dir` still wins, and the default never writes into the caller's cwd.
190
+ The workspace and every config-backed option are resolved and **frozen** by
191
+ `kk stage run itc-prepare` (0303) — never re-derived here. Pre-generate the run id first, so the
192
+ frozen `itc-options.json` binds to the run that will consume it:
192
193
 
193
- Binding (0153): `--auto` binds `auto=true` and defaults `research`, `judge` and `unslop` to
194
+ ```bash
195
+ RUN_ID="kk-itc-$(date +%Y%m%dT%H%M%S)-$$"
196
+ launch="$(mktemp)"
197
+ # Unpassed flags stay "" and are dropped: they fall to workflows.kk-itc config, then compiled defaults.
198
+ jq -n --arg topic "$TOPIC" --arg inputFile "$INPUT_FILE" --arg fromCollect "$FROM_COLLECT" --arg dir "$DIR" \
199
+ --arg parentRunId "$RUN_ID" --arg topicProfile "$TOPIC_PROFILE" --arg runDate "$RUN_DATE" \
200
+ --arg writer "$WRITER" --arg playbook "$PLAYBOOK" --arg sourceLocale "$SOURCE_LOCALE" \
201
+ --arg publishTargets "$TARGETS" --arg publishPreset "$PRESET" --arg seo "$SEO" --arg maxRevisions "$MAX_REVISIONS" \
202
+ --arg research "$RESEARCH" --arg judge "$JUDGE" --arg auto "$AUTO" --arg unslop "$UNSLOP" --arg force "$FORCE" \
203
+ --arg publishLive "$LIVE" --arg coverEnabled "$COVER" --arg inlineImages "$INLINE_IMAGES" \
204
+ '$ARGS.named | with_entries(select(.value != ""))
205
+ | with_entries(if (.key | IN("research","judge","auto","unslop","force","publishLive","coverEnabled","inlineImages"))
206
+ then .value = (.value == "true") else . end)
207
+ | if has("maxRevisions") then .maxRevisions |= tonumber else . end' > "$launch"
208
+ work_dir=$(kk stage run itc-prepare --request "$launch" | jq -r '.workDir')
209
+ ```
210
+
211
+ Exactly one of `topic` | `inputFile` | `fromCollect`. An invalid key, config file or source fails
212
+ before any workspace is written.
213
+
214
+ Binding (0153), applied once by `itc-prepare`: `--auto` binds `auto=true` and defaults `research`, `judge` and `unslop` to
194
215
  `true` unless the operator passed them explicitly. `--live` is never implied — `publish_live`
195
216
  stays `false` unless `--live` is passed explicitly (ADR-022). `--playbook wechat` without
196
217
  `--source-locale` binds `source_locale=zh`; an explicit `--source-locale` always wins.
197
218
  `--targets <a,b>` binds `publish_targets`; `--cover` / `--inline-images` bind
198
219
  `cover_enabled` / `inline_images` (`false` unless passed); `--max-revisions` binds
199
- `max_revisions` (default `2`).
220
+ `max_revisions` (default `2`). Each lands in `itc-options.json` under its camelCase key.
221
+
222
+ **Collected topic (`--from-collect <profile>`, 0299).** `itc-prepare` without `--chosen` returns
223
+ an *intake* snapshot (`status: intake`, `workDir: null`). Choose the topic in a run-scoped intake
224
+ dir, then finalize:
225
+
226
+ 1. `kk stage run itc-topic-extract --request` `{parentRunId, intakeDir, topicProfile}` → the pool.
227
+ 2. The agent writes `$intakeDir/topic-candidates.json` (≤3 candidates `a|b|c` with
228
+ `title`/`reason`/`itemIds` from the pool); `kk stage run itc-topic-validate --request`
229
+ `{parentRunId, intakeDir}` must pass.
230
+ 3. `kk stage run itc-decision --topic "$intakeDir" "$RUN_ID"` prints the chosen id or `defer`.
231
+ **`defer` stops** — ask the operator to pick (never fall back to the first candidate).
232
+ 4. `kk stage run itc-topic-select --request` `{parentRunId, intakeDir, selectedCandidateId}`.
233
+ 5. `work_dir=$(kk stage run itc-prepare --request "$launch" --chosen "$intakeDir" | jq -r .workDir)`
234
+ finalizes the snapshot under the chosen title and copies the selection evidence into the workspace.
200
235
 
201
236
  ### Profile `kk-solo-podcast`
202
237
 
@@ -231,14 +266,17 @@ spur workflow run "$dest" --vars \
231
266
 
232
267
  ### Profile `kk-itc`
233
268
 
234
- Bind vars: `topic`, `input_file`, `work_dir`, `writer`, `playbook`, `research`, `judge`, `auto`,
235
- `unslop`, `max_revisions`, `outline`, `force`, `rubric` (`tech-accuracy`), `agent`,
236
- `cover_enabled`, `inline_images`, `publish_targets`, `publish_live`, `source_locale`. The judge
237
- verdict lands at `$work_dir/.itc-verdict.json` (fixed path inside the workflow).
269
+ Launch under the **same** `RUN_ID` the snapshot was frozen for; the `prepare` node refuses a
270
+ snapshot owned by another run unless `force=true`. Downstream nodes read options from
271
+ `$work_dir/itc-options.json`, so the vars only locate the workspace and carry run-local inputs:
272
+ `work_dir`, `topic_profile`, `publish_preset`, `seo_mode`, `from_collect`, `run_date`, `outline`,
273
+ `force`, `rubric` (`tech-accuracy`), `agent`. The judge verdict lands at
274
+ `$work_dir/.itc-verdict.json`. A live plan with targets pauses at `send-confirm`: resuming
275
+ (`spur workflow continue $RUN_ID`) approves the send, cancelling rejects it.
238
276
 
239
277
  ```bash
240
- spur workflow run "$dest" --vars \
241
- "{\"topic\":\"$TOPIC\",\"input_file\":\"$INPUT_FILE\",\"work_dir\":\"$work_dir\",\"writer\":\"$WRITER\",\"playbook\":\"$PLAYBOOK\",\"research\":\"$RESEARCH\",\"judge\":\"$JUDGE\",\"auto\":\"$AUTO\",\"unslop\":\"$UNSLOP\",\"max_revisions\":\"$MAX_REVISIONS\",\"outline\":\"$OUTLINE\",\"force\":\"$FORCE\",\"rubric\":\"tech-accuracy\",\"agent\":\"$AGENT\",\"cover_enabled\":\"$COVER\",\"inline_images\":\"$INLINE_IMAGES\",\"publish_targets\":\"$TARGETS\",\"publish_live\":\"$LIVE\",\"source_locale\":\"$SOURCE_LOCALE\"}"
278
+ spur workflow run "$dest" --run-id "$RUN_ID" --vars \
279
+ "{\"work_dir\":\"$work_dir\",\"topic_profile\":\"$TOPIC_PROFILE\",\"publish_preset\":\"$PRESET\",\"seo_mode\":\"$SEO\",\"from_collect\":\"$FROM_COLLECT\",\"run_date\":\"$RUN_DATE\",\"outline\":\"$OUTLINE\",\"force\":\"$FORCE\",\"rubric\":\"tech-accuracy\",\"agent\":\"$AGENT\"}"
242
280
  ```
243
281
 
244
282
  ### Profile `kk-solo-podcast`
@@ -0,0 +1,46 @@
1
+ $schema: "@gobing-ai/spur/schemas/decision-catalog.schema.json"
2
+ version: 1
3
+
4
+ defaults:
5
+ minConfidence: 0
6
+
7
+ decisions:
8
+ kk-itc-topic-choice:
9
+ type: choice
10
+ description: Choose the strongest topic candidate from the candidate evidence pool, or defer to operator
11
+ parameters:
12
+ candidates:
13
+ type: string
14
+ description: Formatted candidate options with title, rationale, item IDs and source URLs
15
+ criteria:
16
+ a: Candidate slot a is the strongest and most cohesive technical article topic.
17
+ b: Candidate slot b is the strongest and most cohesive technical article topic.
18
+ c: Candidate slot c is the strongest and most cohesive technical article topic.
19
+ defer: Candidates are insufficient, contradictory, or need explicit operator review.
20
+ fallback: defer
21
+
22
+ kk-itc-outline-choice:
23
+ type: choice
24
+ description: Choose the best structured technical article outline option, or defer to operator
25
+ parameters:
26
+ options:
27
+ type: string
28
+ description: Candidate outline options with structure, headings, and rationale
29
+ criteria:
30
+ a: Outline option a has the strongest narrative arc and technical depth.
31
+ b: Outline option b has the strongest narrative arc and technical depth.
32
+ c: Outline option c has the strongest narrative arc and technical depth.
33
+ defer: Outlines require manual operator adjustment or refinement.
34
+ fallback: defer
35
+
36
+ kk-itc-preset-choice:
37
+ type: choice
38
+ description: Accept the suggested channel publication preset, or publish to no targets by default
39
+ parameters:
40
+ suggestion:
41
+ type: string
42
+ description: The recommended publication preset name and target channels
43
+ criteria:
44
+ suggested: Accept the suggested preset for publication.
45
+ none: Do not use the suggested preset; leave targets unselected.
46
+ fallback: none
@@ -0,0 +1,57 @@
1
+ $schema: "@gobing-ai/spur/schemas/decision-catalog.schema.json"
2
+ version: 1
3
+
4
+ defaults:
5
+ minConfidence: 0
6
+
7
+ decisions:
8
+ kk-seo-keyword-relevance:
9
+ type: score
10
+ description: Relevance of a keyword query to the target page content and topic.
11
+ parameters:
12
+ subject_id:
13
+ type: string
14
+ description: Keyword identifier or query string.
15
+ locale:
16
+ type: string
17
+ description: Target audience locale.
18
+ criteria:
19
+ - Unrelated or tangential keyword query with no relevance.
20
+ - Low relevance or secondary query with modest topical connection.
21
+ - Highly relevant keyword directly addressed by primary page content.
22
+ - Perfect primary target query with comprehensive topical alignment.
23
+ fallback: 0
24
+
25
+ kk-seo-keyword-other-brand:
26
+ type: noul
27
+ description: Whether the keyword query references a competitor or third-party brand.
28
+ parameters:
29
+ subject_id:
30
+ type: string
31
+ description: Keyword identifier or query string.
32
+ locale:
33
+ type: string
34
+ description: Target audience locale.
35
+ criteria:
36
+ "true": Query contains an explicit competitor brand, trademark, or proprietary product name.
37
+ "false": Query consists of generic, unbranded or owned brand search terms.
38
+ fallback: false
39
+
40
+ kk-seo-keyword-page:
41
+ type: choice
42
+ description: Mapping strategy for targeting this keyword query across site pages.
43
+ parameters:
44
+ subject_id:
45
+ type: string
46
+ description: Keyword identifier or query string.
47
+ locale:
48
+ type: string
49
+ description: Target audience locale.
50
+ candidates:
51
+ type: json
52
+ description: Serialized list of candidate page titles and URLs (max 30 candidates).
53
+ criteria:
54
+ existing: Map keyword to an existing candidate page that already best satisfies intent.
55
+ new: Create a dedicated new landing page or article to satisfy search intent.
56
+ unclear: Ambiguous intent, low commercial relevance, or insufficient evidence to map.
57
+ fallback: unclear
@@ -0,0 +1,230 @@
1
+ $schema: "@gobing-ai/spur/schemas/decision-catalog.schema.json"
2
+ version: 1
3
+
4
+ defaults:
5
+ minConfidence: 0
6
+
7
+ decisions:
8
+ kk-seo-page-type:
9
+ type: choice
10
+ description: Functional structural role of this webpage within the site.
11
+ parameters:
12
+ subject_id:
13
+ type: string
14
+ description: Canonical URL of the page.
15
+ locale:
16
+ type: string
17
+ description: Target audience locale.
18
+ criteria:
19
+ homepage: Primary domain root or brand entry point.
20
+ product: Specific item, merchandise or standalone paid SKU.
21
+ category: Collection, catalogue index, listing or facet filter page.
22
+ service: Specific professional or client service offering.
23
+ article: Editorial blog post, knowledge article, guide or news story.
24
+ landing: Targeted campaign, lead generation or conversion page.
25
+ about: Organization background, team or company history.
26
+ contact: Communication, physical location or support contact page.
27
+ policy: Terms of service, privacy policy or legal disclosure.
28
+ other: Functional page outside the standard classifications.
29
+ unclear: Ambiguous, mixed, or insufficient content signals.
30
+ fallback: unclear
31
+
32
+ kk-seo-search-intent:
33
+ type: choice
34
+ description: Primary user search intent targeted by this page.
35
+ parameters:
36
+ subject_id:
37
+ type: string
38
+ description: Canonical URL of the page.
39
+ locale:
40
+ type: string
41
+ description: Target audience locale.
42
+ criteria:
43
+ informational: User seeks knowledge, explanations, answers or tutorials.
44
+ navigational: User seeks to locate a specific brand, tool or destination.
45
+ commercial: User evaluates options, reviews, comparisons or solutions.
46
+ transactional: User is ready to purchase, subscribe, sign up or download.
47
+ mixed: Multiple distinct search intents addressed equally.
48
+ unclear: Ambiguous or undefined user intent.
49
+ fallback: unclear
50
+
51
+ kk-seo-page-action:
52
+ type: choice
53
+ description: Strategic editorial action recommended for this page.
54
+ parameters:
55
+ subject_id:
56
+ type: string
57
+ description: Canonical URL of the page.
58
+ locale:
59
+ type: string
60
+ description: Target audience locale.
61
+ criteria:
62
+ keep-or-improve: Valuable page worth retaining and updating incrementally.
63
+ rewrite: Substantive content overhaul required to meet search intent.
64
+ review-consolidation: Potential cannibalization or thin page to consolidate.
65
+ unclear: Insufficient evidence to determine strategic page action.
66
+ fallback: unclear
67
+
68
+ kk-seo-importance:
69
+ type: score
70
+ description: Strategic importance of this page to site search authority.
71
+ parameters:
72
+ subject_id:
73
+ type: string
74
+ description: Canonical URL of the page.
75
+ locale:
76
+ type: string
77
+ description: Target audience locale.
78
+ criteria:
79
+ - Incidental or utility page with negligible search potential.
80
+ - Secondary supporting page with modest targeted relevance.
81
+ - Core pillar or commercial page driving regular search authority.
82
+ - Essential primary asset or high-converting flagship page.
83
+ fallback: 0
84
+
85
+ kk-seo-helpfulness:
86
+ type: score
87
+ description: Depth and utility of the page content for human visitors.
88
+ parameters:
89
+ subject_id:
90
+ type: string
91
+ description: Canonical URL of the page.
92
+ locale:
93
+ type: string
94
+ description: Target audience locale.
95
+ criteria:
96
+ - Thin, repetitive or boilerplate text offering little value.
97
+ - Basic overview covering surface facts without original insight.
98
+ - Thorough and genuinely helpful content with practical utility.
99
+ - Exceptional, comprehensive resource with expert depth and clarity.
100
+ fallback: 0
101
+
102
+ kk-seo-specificity:
103
+ type: score
104
+ description: Precision and concrete focus of the topic discussion.
105
+ parameters:
106
+ subject_id:
107
+ type: string
108
+ description: Canonical URL of the page.
109
+ locale:
110
+ type: string
111
+ description: Target audience locale.
112
+ criteria:
113
+ - Vague, generic claims with no specific details or examples.
114
+ - Mostly general statements with a few isolated specifics.
115
+ - Concrete discussion with precise details, steps or terminology.
116
+ - Highly rigorous and exact specification with actionable details.
117
+ fallback: 0
118
+
119
+ kk-seo-trust:
120
+ type: score
121
+ description: Credibility, evidence and verifiable signals of expertise.
122
+ parameters:
123
+ subject_id:
124
+ type: string
125
+ description: Canonical URL of the page.
126
+ locale:
127
+ type: string
128
+ description: Target audience locale.
129
+ criteria:
130
+ - Unsubstantiated claims with zero sourcing or author attribution.
131
+ - Modest credibility signals with basic organizational context.
132
+ - Clear author expertise, cited sources, and verifiable claims.
133
+ - Authoritative industry benchmark with transparent methodology.
134
+ fallback: 0
135
+
136
+ kk-seo-citability:
137
+ type: score
138
+ description: Ease and likelihood of being quoted by answer engines.
139
+ parameters:
140
+ subject_id:
141
+ type: string
142
+ description: Canonical URL of the page.
143
+ locale:
144
+ type: string
145
+ description: Target audience locale.
146
+ criteria:
147
+ - Prose structure is unfocused and difficult to extract citations from.
148
+ - Contains extractable points buried in dense conversational prose.
149
+ - Clear, concise statements directly answering user questions.
150
+ - Structured, highly quotable definitions, data points and conclusions.
151
+ fallback: 0
152
+
153
+ kk-seo-title-fit:
154
+ type: score
155
+ description: How accurately the HTML title describes the page substance.
156
+ parameters:
157
+ subject_id:
158
+ type: string
159
+ description: Canonical URL of the page.
160
+ locale:
161
+ type: string
162
+ description: Target audience locale.
163
+ criteria:
164
+ - Title is misleading, irrelevant or clickbait disconnected from body.
165
+ - Title loosely matches body but misses primary topic emphasis.
166
+ - Title accurately reflects main subject and matches search intent.
167
+ - Title is concise, compelling and perfectly aligned with page content.
168
+ fallback: 0
169
+
170
+ kk-seo-meta-fit:
171
+ type: score
172
+ description: How accurately the meta description summarizes the page content.
173
+ parameters:
174
+ subject_id:
175
+ type: string
176
+ description: Canonical URL of the page.
177
+ locale:
178
+ type: string
179
+ description: Target audience locale.
180
+ criteria:
181
+ - Meta description is missing, generic or irrelevant.
182
+ - Description is partially relevant but lacks distinct proposition.
183
+ - Description clearly summarizes key page value and answers intent.
184
+ - Description is compelling, perfectly accurate and snippet-ready.
185
+ fallback: 0
186
+
187
+ kk-seo-answer-first:
188
+ type: noul
189
+ description: Whether the page answers the primary question early before the fold.
190
+ parameters:
191
+ subject_id:
192
+ type: string
193
+ description: Canonical URL of the page.
194
+ locale:
195
+ type: string
196
+ description: Target audience locale.
197
+ criteria:
198
+ "true": Direct answer or core conclusion appears in the opening section.
199
+ "false": Direct answer is buried deep after extensive filler or intro prose.
200
+ fallback: false
201
+
202
+ kk-seo-next-step:
203
+ type: noul
204
+ description: Whether the page guides the reader toward a clear next step or CTA.
205
+ parameters:
206
+ subject_id:
207
+ type: string
208
+ description: Canonical URL of the page.
209
+ locale:
210
+ type: string
211
+ description: Target audience locale.
212
+ criteria:
213
+ "true": Identifiable next action, link or relevant resource provided.
214
+ "false": Dead-end page with no contextual navigation or clear next step.
215
+ fallback: false
216
+
217
+ kk-seo-heading-fit:
218
+ type: noul
219
+ description: Whether the heading hierarchy accurately mirrors page topic flow.
220
+ parameters:
221
+ subject_id:
222
+ type: string
223
+ description: Canonical URL of the page.
224
+ locale:
225
+ type: string
226
+ description: Target audience locale.
227
+ criteria:
228
+ "true": Headings outline a coherent logical structure matching body sections.
229
+ "false": Headings are disorganized, misleading, decorative or missing.
230
+ fallback: false
@@ -0,0 +1,21 @@
1
+ $schema: "@gobing-ai/spur/schemas/decision-catalog.schema.json"
2
+ version: 1
3
+
4
+ defaults:
5
+ minConfidence: 0
6
+
7
+ decisions:
8
+ kk-seo-intent-overlap:
9
+ type: noul
10
+ description: Whether two sampled pages target substantially identical search intent and topic.
11
+ parameters:
12
+ subject_id:
13
+ type: string
14
+ description: Sorted pair identifier (pageA_id::pageB_id).
15
+ locale:
16
+ type: string
17
+ description: Target audience locale.
18
+ criteria:
19
+ "true": Both pages compete for the same primary query intent and cannibalize search authority.
20
+ "false": Pages serve distinct user queries or complementary stages of user intent.
21
+ fallback: false