@gobing-ai/knowledge-kit 0.0.36 → 0.0.37

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.36",
9
+ "version": "0.0.37",
10
10
  "name": "kk",
11
11
  "source": "./plugins/kk",
12
12
  "description": "knowledge-kit — a set of toolkit for knowledge management"
package/dist/index.js CHANGED
@@ -37325,7 +37325,7 @@ ${section}
37325
37325
  `;
37326
37326
  }
37327
37327
  function selectMissingRuns(entries, markdown) {
37328
- return entries.filter((entry) => entry.status !== "running" && !ledgerHasRun(markdown, entry.runId)).sort((left, right) => (left.startedAt ?? "").localeCompare(right.startedAt ?? "")).map((entry) => entry.runId);
37328
+ return entries.filter((entry) => entry.status !== "running" && entry.isDryRun !== true && !ledgerHasRun(markdown, entry.runId)).sort((left, right) => (left.startedAt ?? "").localeCompare(right.startedAt ?? "")).map((entry) => entry.runId);
37329
37329
  }
37330
37330
  function rowCells(row) {
37331
37331
  const cells = row.replaceAll("\\|", ESCAPED_PIPE_SENTINEL).split("|");
@@ -47322,9 +47322,9 @@ function registerExecutorCommand(program) {
47322
47322
  withRunOptions(executor.command("run").description("Invoke a plugin with input/output files and contract validation").argument("<plugin>", "Name of the plugin to invoke")).action(runInvokeAction);
47323
47323
  withFaninOptions(executor.command("fan-in").alias("fanin").description("Merge N Doc[] JSON inputs into one deduplicated Doc[] output")).action(runFaninAction);
47324
47324
  withFanoutOptions(executor.command("fan-out").alias("fanout").description("Publish one Content JSON to N targets, aggregating per-target results")).action(runFanoutAction);
47325
- withRunOptions(program.command("invoke", { hidden: true }).description("Alias for `executor run` (deprecated)").argument("<plugin>", "Name of the plugin to invoke")).action(runInvokeAction);
47326
- withFaninOptions(program.command("fanin", { hidden: true }).description("Alias for `executor fan-in` (deprecated)")).action(runFaninAction);
47327
- withFanoutOptions(program.command("fanout", { hidden: true }).description("Alias for `executor fan-out` (deprecated)")).action(runFanoutAction);
47325
+ withRunOptions(program.command("invoke", { hidden: true }).description("[deprecated] Alias for `executor run`").argument("<plugin>", "Name of the plugin to invoke")).action(runInvokeAction);
47326
+ withFaninOptions(program.command("fanin", { hidden: true }).description("[deprecated] Alias for `executor fan-in`")).action(runFaninAction);
47327
+ withFanoutOptions(program.command("fanout", { hidden: true }).description("[deprecated] Alias for `executor fan-out`")).action(runFanoutAction);
47328
47328
  }
47329
47329
 
47330
47330
  // src/cli.ts
@@ -47381,9 +47381,10 @@ async function lockReleaseAction(options) {
47381
47381
  }
47382
47382
  }
47383
47383
  function registerLockCommand(parent, hidden = false) {
47384
- const lock = parent.command("lock", { hidden }).description("Single-flight locks over the store");
47385
- lock.command("acquire").description("Take the named lock for this run; prints {acquired} or {skipped, holder} (exit 0 both ways)").requiredOption("--name <lock>", "Lock name (e.g. collect)").requiredOption("--run <id>", "Owning run id (re-entrant for the same id)").option("--ttl <ms>", "Claim lifetime for a dead holder, in milliseconds", String(DEFAULT_LOCK_TTL_MS)).option("--db <path>", "Store database path (default: kk config store.db_path, else .works/kk.db)").action(lockAcquireAction);
47386
- lock.command("release").description("Release the named lock iff this run still owns it; prints {released}").requiredOption("--name <lock>", "Lock name (e.g. collect)").requiredOption("--run <id>", "Owning run id").option("--db <path>", "Store database path (default: kk config store.db_path, else .works/kk.db)").action(lockReleaseAction);
47384
+ const legacy = hidden ? "[deprecated] " : "";
47385
+ const lock = parent.command("lock", { hidden }).description(`${legacy}Single-flight locks over the store`);
47386
+ lock.command("acquire").description(`${legacy}Take the named lock for this run; prints {acquired} or {skipped, holder} (exit 0 both ways)`).requiredOption("--name <lock>", "Lock name (e.g. collect)").requiredOption("--run <id>", "Owning run id (re-entrant for the same id)").option("--ttl <ms>", "Claim lifetime for a dead holder, in milliseconds", String(DEFAULT_LOCK_TTL_MS)).option("--db <path>", "Store database path (default: kk config store.db_path, else .works/kk.db)").action(lockAcquireAction);
47387
+ lock.command("release").description(`${legacy}Release the named lock iff this run still owns it; prints {released}`).requiredOption("--name <lock>", "Lock name (e.g. collect)").requiredOption("--run <id>", "Owning run id").option("--db <path>", "Store database path (default: kk config store.db_path, else .works/kk.db)").action(lockReleaseAction);
47387
47388
  }
47388
47389
 
47389
47390
  // src/commands/plugin.ts
@@ -47590,12 +47591,12 @@ function registerPluginCommand(program) {
47590
47591
  registerStatusVerb(plugin, "status", "List discovered plugins grouped by kind with a per-plugin health mark", false);
47591
47592
  plugin.command("check [names...]").description("Run each selected plugin\u2019s readiness probe; exits nonzero on refused or errored plugins").option("--kind <kind>", "Filter plugins by kind (ingestion, generator, publish)").option("--json", "Output machine-readable verdict rows").option("--headed", "Run probes with a visible browser window").option("--timeout <ms>", "Per-plugin probe timeout in milliseconds").option("--plugins-root <dir>", "Exclusive root containing plugins (isolation/tests only)").option("--plugins-path <dir>", "Extra plugin root to union with defaults (repeatable)", collectPath, []).action(checkAction);
47592
47593
  plugin.command("login <name>").description("Run the plugin\u2019s interactive login probe with inherited stdio").option("--plugins-root <dir>", "Exclusive root containing plugins (isolation/tests only)").option("--plugins-path <dir>", "Extra plugin root to union with defaults (repeatable)", collectPath, []).action(loginAction);
47593
- registerStatusVerb(plugin, "list", "Alias for `plugin status` (deprecated)", true);
47594
- registerStatusVerb(plugin, "doctor", "Alias for `plugin status` (deprecated)", true);
47595
- const plugins = program.command("plugins", { hidden: true }).description("Alias for `plugin status` (deprecated)");
47596
- registerStatusVerb(plugins, "status", "Alias for `plugin status` (deprecated)", true);
47597
- registerStatusVerb(plugins, "list", "Alias for `plugin status` (deprecated)", true);
47598
- registerStatusVerb(plugins, "doctor", "Alias for `plugin status` (deprecated)", true);
47594
+ registerStatusVerb(plugin, "list", "[deprecated] Alias for `plugin status`", true);
47595
+ registerStatusVerb(plugin, "doctor", "[deprecated] Alias for `plugin status`", true);
47596
+ const plugins = program.command("plugins", { hidden: true }).description("[deprecated] Alias for `plugin status`");
47597
+ registerStatusVerb(plugins, "status", "[deprecated] Alias for `plugin status`", true);
47598
+ registerStatusVerb(plugins, "list", "[deprecated] Alias for `plugin status`", true);
47599
+ registerStatusVerb(plugins, "doctor", "[deprecated] Alias for `plugin status`", true);
47599
47600
  }
47600
47601
 
47601
47602
  // src/commands/schedule.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/knowledge-kit",
3
- "version": "0.0.36",
3
+ "version": "0.0.37",
4
4
  "description": "An ingest → create → publish content pipeline CLI (Bun).",
5
5
  "repository": {
6
6
  "type": "git",
@@ -12582,6 +12582,24 @@ function parseStyleContract(markdown) {
12582
12582
  }
12583
12583
  return StyleContractSchema.parse(parsed);
12584
12584
  }
12585
+ var REDUNDANT_INSIGHT_SPAN = 14;
12586
+ var REDUNDANT_INSIGHT_CLAUSE_LIMIT = 3;
12587
+ function redundantInsightClauses(insight, reference) {
12588
+ const ref = reference.replace(/[\s\p{P}\p{S}]+/gu, "");
12589
+ let count = 0;
12590
+ for (const clause of insight.split(/[\u3002\uFF01\uFF1F\uFF1B\uFF0C]/)) {
12591
+ const norm = clause.replace(/[\s\p{P}\p{S}]+/gu, "");
12592
+ if (norm.length < REDUNDANT_INSIGHT_SPAN)
12593
+ continue;
12594
+ for (let i = 0;i + REDUNDANT_INSIGHT_SPAN <= norm.length; i++) {
12595
+ if (ref.includes(norm.slice(i, i + REDUNDANT_INSIGHT_SPAN))) {
12596
+ count++;
12597
+ break;
12598
+ }
12599
+ }
12600
+ }
12601
+ return count;
12602
+ }
12585
12603
  function validateScriptPlan(plan, docs, style) {
12586
12604
  const parsed = ScriptPlanSchema.safeParse(plan);
12587
12605
  if (!parsed.success) {
@@ -12615,6 +12633,10 @@ function validateScriptPlan(plan, docs, style) {
12615
12633
  errors.push({ itemId: item.id, rule: "cap:lead" });
12616
12634
  if (item.insight.length > style.caps.insight)
12617
12635
  errors.push({ itemId: item.id, rule: "cap:insight" });
12636
+ const doc = docs.find((entry) => entry.id === item.id);
12637
+ if (doc !== undefined && redundantInsightClauses(item.insight, `${item.lead}${doc.body}`) >= REDUNDANT_INSIGHT_CLAUSE_LIMIT) {
12638
+ errors.push({ itemId: item.id, rule: "redundant-insight" });
12639
+ }
12618
12640
  }
12619
12641
  const fields = [
12620
12642
  { itemId: "episode", text: valid.intro },
@@ -104,9 +104,33 @@ export function parseStyleContract(markdown: string): StyleContract {
104
104
  * - `cap:<field>`: a field over its cap (`intro`/`outro` report `itemId: episode`)
105
105
  * - `banned:<phrase>`: a field containing a banned phrase
106
106
  * - `duplicate-insight`: the same insight text on two items in one episode
107
+ * - `redundant-insight`: the insight restates the narration — ≥ 3 of its clauses each share a
108
+ * ≥ 14-char verbatim span with the item's spoken facts (lead + doc body). 20261014 episode: the
109
+ * prompt told the planner to restate facts, so the listener heard near-identical wording twice
110
+ * per item. One repeated number is tolerable; three copied clauses is a second narration.
107
111
  *
108
112
  * All-or-nothing by contract (Q&A): a partially valid plan is never partially applied.
109
113
  */
114
+ /** Verbatim-span length (normalized) that counts a clause as restated, not as a fact mention. */
115
+ const REDUNDANT_INSIGHT_SPAN = 14;
116
+ /** A listener tolerates one restated clause; at this many the item reads as a second narration. */
117
+ const REDUNDANT_INSIGHT_CLAUSE_LIMIT = 3;
118
+
119
+ function redundantInsightClauses(insight: string, reference: string): number {
120
+ const ref = reference.replace(/[\s\p{P}\p{S}]+/gu, '');
121
+ let count = 0;
122
+ for (const clause of insight.split(/[。!?;,]/)) {
123
+ const norm = clause.replace(/[\s\p{P}\p{S}]+/gu, '');
124
+ if (norm.length < REDUNDANT_INSIGHT_SPAN) continue;
125
+ for (let i = 0; i + REDUNDANT_INSIGHT_SPAN <= norm.length; i++) {
126
+ if (ref.includes(norm.slice(i, i + REDUNDANT_INSIGHT_SPAN))) {
127
+ count++;
128
+ break;
129
+ }
130
+ }
131
+ }
132
+ return count;
133
+ }
110
134
  export function validateScriptPlan(plan: unknown, docs: Doc[], style: StyleContract): ScriptPlanValidation {
111
135
  const parsed = ScriptPlanSchema.safeParse(plan);
112
136
  if (!parsed.success) {
@@ -134,6 +158,14 @@ export function validateScriptPlan(plan: unknown, docs: Doc[], style: StyleContr
134
158
  for (const item of valid.items) {
135
159
  if (item.lead.length > style.caps.lead) errors.push({ itemId: item.id, rule: 'cap:lead' });
136
160
  if (item.insight.length > style.caps.insight) errors.push({ itemId: item.id, rule: 'cap:insight' });
161
+ const doc = docs.find((entry) => entry.id === item.id);
162
+ // unknown-id is already reported above; skip the redundancy check rather than double-report.
163
+ if (
164
+ doc !== undefined &&
165
+ redundantInsightClauses(item.insight, `${item.lead}${doc.body}`) >= REDUNDANT_INSIGHT_CLAUSE_LIMIT
166
+ ) {
167
+ errors.push({ itemId: item.id, rule: 'redundant-insight' });
168
+ }
137
169
  }
138
170
 
139
171
  const fields: { itemId: string; text: string }[] = [
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "kk",
3
- "version": "0.0.36",
3
+ "version": "0.0.37",
4
4
  "description": "knowledge-kit Claude Code plugin: skills, commands, subagents, hooks, and rules"
5
5
  }
@@ -29,7 +29,7 @@
29
29
 
30
30
  - `intro`:开场导语,串起今天几个主题,接在固定问候之后,不超过 150 字。
31
31
  - `lead`:一句话口语桥段,引出下一个主题的【标题】;只做铺垫,不得复述标题本身,不超过 80 字。
32
- - `insight`:只基于这一条自身可核事实的点评(3–6 句),展开这一条自身的机制→意义→实用角度;不得挪用其他条目的话题,不超过 380 字。时长契约:全片按 15–20 分钟口播设计,insight 应写到接近上限——宁可深挖这一条的事实,也不要浅尝辄止。字数按配音驱动折算:gemini ≈3.3 字/秒(正文约 4300–5800 字);Voicebox robin-news ≈6.3 字/秒(正文约 5700–7600 字,或增加条目数)。
32
+ - `insight`:只基于这一条自身可核事实的点评(3–6 句),展开这一条自身的机制→意义→实用角度;不得挪用其他条目的话题,不超过 380 字。听众刚听完这一条的事实叙述,insight 是点评不是复读:不得复述正文已讲过的事实、数字或成串措辞(最多点到一两个关键词),深度来自角度和判断,不是把事实再讲一遍。时长契约:全片按 15–20 分钟口播设计,insight 应写到接近上限——宁可深挖这一条的角度,也不要浅尝辄止。字数按配音驱动折算:gemini ≈3.3 字/秒(正文约 4300–5800 字);Voicebox robin-news ≈6.3 字/秒(正文约 5700–7600 字,或增加条目数)。
33
33
  - `outro`:简短收束语,接在固定致谢之前,不超过 150 字。
34
34
 
35
35
  ## 机器可读契约
@@ -457,7 +457,7 @@ nodes:
457
457
  # ADR-115 error tier: keep this prompt <= 1000 chars.
458
458
  input: |
459
459
  Read ${vars.work_dir}/21-plan/${vars.run_date}_03_plan_plan.json — a JSON array of docs; don't edit it. Also read the style contract ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_style.md and obey its char caps, duration budget and banned phrases.
460
- For each doc write a `lead` (a spoken bridge, not containing the title) and an `insight` (grounded only in that doc's facts: keep its numbers and hedges(据报道), add no new degree words).
460
+ For each doc write a `lead` (a spoken bridge, not containing the title) and an `insight` (a take, not a recap: the narration just read that doc's facts — never restate its facts, numbers or wording; give only your angle, judgment or what-to-watch, no new facts, keep hedges(据报道), no new degree words).
461
461
  intro/outro hook only in-scope items; no greeting or sign-off — the builder adds both. 选题边界:政治/外交、贸易管制、军事、公共政策争议、社会公共事件条目绝不复述;读文章 22-article/*_06_article-write_body.md,其文末注记点名的 id 一并进 excluded,各配中文原因。
462
462
  Output only JSON {"version":1,"intro":"…","outro":"…","items":[{"id":"…","lead":"…","insight":"…"}],"excluded":[{"id":"…","reason":"…"}]} to ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_plan.json. Exactly one `items` entry per doc not in `excluded`.
463
463
  expectFile: ${vars.work_dir}/24-script/${vars.run_date}_08_script-write_plan.json