@gobing-ai/knowledge-kit 0.0.30 → 0.0.31

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.
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "plugins": [
8
8
  {
9
- "version": "0.0.30",
9
+ "version": "0.0.31",
10
10
  "name": "kk",
11
11
  "source": "./plugins/kk",
12
12
  "description": "knowledge-kit — a set of toolkit for knowledge management"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/knowledge-kit",
3
- "version": "0.0.30",
3
+ "version": "0.0.31",
4
4
  "description": "An ingest → create → publish content pipeline CLI (Bun).",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21994,7 +21994,7 @@ function normalizeBroadcastText(raw, isZh) {
21994
21994
  text = text.replace(/\s+/g, " ");
21995
21995
  if (isZh) {
21996
21996
  text = text.replace(/[''\u2018\u2019]/g, "");
21997
- text = text.replace(/[:\uFF1A]/g, "\uFF0C");
21997
+ text = text.replace(/[:\uFF1A](?!\/\/)/g, "\uFF0C");
21998
21998
  text = text.replace(/[;\uFF1B]/g, "\u3002");
21999
21999
  text = text.replace(/--+/g, "\uFF0C");
22000
22000
  text = text.replace(/\.{2,}/g, "\u3002");
@@ -119,8 +119,10 @@ export function normalizeBroadcastText(raw: string, isZh: boolean): string {
119
119
 
120
120
  if (isZh) {
121
121
  text = text.replace(/[''‘’]/g, '');
122
- // Replace English punctuation with Chinese pauses
123
- text = text.replace(/[::]/g, ',');
122
+ // Replace English punctuation with Chinese pauses. A URL scheme's colon (`https://`,
123
+ // `wss://`) is the exception: spoken as `,//` the URL is mangled, and the content gate
124
+ // (`validate-voicescript.ts` CONTENT_URL_MANGLE_RE) rejects the whole run over it.
125
+ text = text.replace(/[::](?!\/\/)/g, ',');
124
126
  text = text.replace(/[;;]/g, '。');
125
127
  text = text.replace(/--+/g, ',');
126
128
  text = text.replace(/\.{2,}/g, '。');
@@ -22118,8 +22118,18 @@ function passesGates(doc2, planDate) {
22118
22118
  return true;
22119
22119
  return published >= Date.parse(planDate) - FRESHNESS_DAYS * 86400000;
22120
22120
  }
22121
+ var SCAFFOLD_HEADING_RE = /^\s*\*\*\s*[\u300C\u300E][^\u300D\u300F\n]{1,80}[\u300D\u300F]\s*\*\*\s*[\uFF1A:]?(?:\s+|$)/;
22121
22122
  function cleanBody(body) {
22122
- return body.replace(/\u2B50\uFE0F\s*[\d.]+\s*\/\s*10/g, "").replace(/^#{1,6}\s*\[[^\]]*\]\([^)]*\)\s*$/gm, "").replace(/\n{3,}/g, `
22123
+ const kept = [];
22124
+ for (const line of body.split(`
22125
+ `)) {
22126
+ const stripped = line.replace(SCAFFOLD_HEADING_RE, "");
22127
+ if (stripped.trim() === "" && line.trim() !== "")
22128
+ continue;
22129
+ kept.push(stripped);
22130
+ }
22131
+ return kept.join(`
22132
+ `).replace(/\u2B50\uFE0F\s*[\d.]+\s*\/\s*10/g, "").replace(/^#{1,6}\s*\[[^\]]*\]\([^)]*\)\s*$/gm, "").replace(/\n{3,}/g, `
22123
22133
 
22124
22134
  `).trim();
22125
22135
  }
@@ -248,9 +248,30 @@ function passesGates(doc: Doc, planDate: string): boolean {
248
248
  return published >= Date.parse(planDate) - FRESHNESS_DAYS * 86_400_000;
249
249
  }
250
250
 
251
- /** Strip `⭐️ x.y/10` ratings and `### [title](url)` heading copies from a body. */
251
+ /**
252
+ * Research-note section headings: the horizon/last30days note format labels its sections with a
253
+ * bold corner-bracket heading (`**「社区讨论」** 评论区…`, `**「背景」**`, `**「背景:GCN 架构」**`,
254
+ * `**「方案」**`, …). Spoken from a VoiceScript segment they read as scaffolding, and the content
255
+ * gates reject a segment that opens with one (`voice-gen`'s `voicescript.ts`, the
256
+ * `validate-voicescript.ts` sidecar) — which fails the whole unattended daily run at the `script`
257
+ * state (2026-10-03 「背景」/「社区讨论」, 2026-10-04 「社区讨论」). The bold corner-bracket markup is
258
+ * the discriminator: these note bodies never bold-quote conversational prose, so the label list
259
+ * stays open-ended.
260
+ */
261
+ const SCAFFOLD_HEADING_RE = /^\s*\*\*\s*[「『][^」』\n]{1,80}[」』]\s*\*\*\s*[::]?(?:\s+|$)/;
262
+
263
+ /** Strip `⭐️ x.y/10` ratings, `### [title](url)` heading copies and `**「label」**` research-note
264
+ * section headings from a body. A heading ahead of prose keeps the prose; a heading alone on its
265
+ * line disappears with it. */
252
266
  export function cleanBody(body: string): string {
253
- return body
267
+ const kept: string[] = [];
268
+ for (const line of body.split('\n')) {
269
+ const stripped = line.replace(SCAFFOLD_HEADING_RE, '');
270
+ if (stripped.trim() === '' && line.trim() !== '') continue; // the line held only a heading
271
+ kept.push(stripped);
272
+ }
273
+ return kept
274
+ .join('\n')
254
275
  .replace(/⭐️\s*[\d.]+\s*\/\s*10/g, '')
255
276
  .replace(/^#{1,6}\s*\[[^\]]*\]\([^)]*\)\s*$/gm, '')
256
277
  .replace(/\n{3,}/g, '\n\n')
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "kk",
3
- "version": "0.0.30",
3
+ "version": "0.0.31",
4
4
  "description": "knowledge-kit Claude Code plugin: skills, commands, subagents, hooks, and rules"
5
5
  }
@@ -81,8 +81,10 @@ outside `HIGH|MEDIUM|LOW` is not a usable verdict anywhere:
81
81
  a valid confidence; an absent verdict file keeps the dry-run convention.
82
82
 
83
83
  Add that filter to any new judge gate; `references/workflow-integration.md` carries the pattern and
84
- `plugins/kk/tests/verdict-confidence.test.ts` fails when a workflow accepts a judge `PASS` without
85
- checking the enum.
84
+ `plugins/kk/tests/verdict-confidence.test.ts` fails on either half of the contract: a workflow that
85
+ accepts a judge `PASS` without checking the enum, and this skill drifting out of the producer side
86
+ (enum line, per-level definition, or a reference snippet whose filter no longer matches the guards
87
+ the consumer scan collects).
86
88
 
87
89
  The level itself does not change routing — escalation stays owned by the review's `RISK` decisions
88
90
  and `escalate` flag; treat `LOW` as a signal to demand human review before publishing.
@@ -35,7 +35,8 @@ if [ ! -f "$verdict_path" ]; then
35
35
  fi
36
36
  # Confidence-aware read: a PASS without a HIGH|MEDIUM|LOW confidence becomes `PASS!`, which no
37
37
  # accept guard matches and the catch-all below rejects. Keep this filter byte-identical wherever a
38
- # guard reads a judge verdict (`plugins/kk/tests/verdict-confidence.test.ts` enforces that).
38
+ # guard reads a judge verdict — and to this snippet: `plugins/kk/tests/verdict-confidence.test.ts`
39
+ # collects every guard's filter plus this one and fails unless they are byte-identical.
39
40
  verdict=$(jq -r 'if (.verdict // "") == "PASS" then (.verdict + (if ((.confidence // "") | test("^(HIGH|MEDIUM|LOW)$")) then "" else "!" end)) else (.verdict // "") end' "$verdict_path")
40
41
  case "$verdict" in
41
42
  PASS)