@gobing-ai/knowledge-kit 0.0.7 → 0.0.9
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/dist/index.js +21604 -11844
- package/package.json +4 -1
- package/plugins/generations/content-gen/package.json +2 -1
- package/plugins/generations/content-gen/src/index.ts +9 -8
- package/plugins/generations/content-gen/src/storm.ts +12 -4
- package/plugins/generations/dailynews-gen/package.json +17 -0
- package/plugins/generations/dailynews-gen/plugin.json +7 -0
- package/plugins/generations/dailynews-gen/src/index.ts +111 -0
- package/plugins/generations/dailynews-gen/src/script-builder.ts +121 -0
- package/plugins/generations/dailynews-gen/tsconfig.json +4 -0
- package/plugins/generations/voice-gen/package.json +17 -0
- package/plugins/generations/voice-gen/plugin.json +6 -0
- package/plugins/generations/voice-gen/src/concat.ts +218 -0
- package/plugins/generations/voice-gen/src/index.ts +228 -0
- package/plugins/generations/voice-gen/src/mp3.ts +65 -0
- package/plugins/generations/voice-gen/src/voicebox-client.ts +223 -0
- package/plugins/generations/voice-gen/src/voicescript.ts +365 -0
- package/plugins/generations/voice-gen/tsconfig.json +8 -0
- package/plugins/ingestions/aihot-ingest/package.json +17 -0
- package/plugins/ingestions/aihot-ingest/plugin.json +7 -0
- package/plugins/ingestions/aihot-ingest/src/client.ts +185 -0
- package/plugins/ingestions/aihot-ingest/src/index.ts +137 -0
- package/plugins/ingestions/aihot-ingest/src/mapper.ts +42 -0
- package/plugins/ingestions/aihot-ingest/tsconfig.json +4 -0
- package/plugins/ingestions/karakeep-local/package.json +17 -0
- package/plugins/ingestions/karakeep-local/src/index.ts +31 -26
- package/plugins/ingestions/karakeep-local/tsconfig.json +4 -0
- package/plugins/ingestions/web-search/package.json +4 -1
- package/plugins/ingestions/web-search/src/index.ts +139 -16
- package/plugins/kk/README.md +9 -3
- package/plugins/kk/commands/workflow-run.md +100 -28
- package/plugins/kk/config.example.yaml +34 -0
- package/plugins/kk/scripts/render-md.ts +8 -3
- package/plugins/kk/skills/{judge → content-judge}/SKILL.md +13 -14
- package/plugins/kk/skills/{judge → content-judge}/references/workflow-integration.md +13 -11
- package/plugins/kk/skills/itc-generating/SKILL.md +147 -0
- package/plugins/kk/skills/itc-generating/references/generic-craft.md +80 -0
- package/plugins/kk/skills/itc-generating/references/platform-english.md +72 -0
- package/plugins/kk/skills/itc-generating/references/platform-wechat.md +60 -0
- package/plugins/kk/skills/itc-generating/references/skill-authoring.md +62 -0
- package/plugins/kk/skills/storm-research/SKILL.md +10 -3
- package/plugins/kk/workflows/judge-gated-publish-example.yaml +101 -0
- package/plugins/kk/workflows/kk-daily-ai-voice.yaml +144 -0
- package/plugins/kk/workflows/kk-ingest-generate-publish.yaml +72 -0
- package/plugins/kk/workflows/kk-itc.yaml +285 -0
- package/plugins/kk/workflows/kk-solo-podcast.yaml +374 -0
- package/plugins/kk/workflows/validate-voicescript.ts +226 -0
- package/plugins/publishings/emdash-pub/package.json +17 -0
- package/plugins/publishings/emdash-pub/plugin.json +7 -0
- package/plugins/publishings/emdash-pub/src/index.ts +450 -0
- package/plugins/publishings/emdash-pub/tsconfig.json +4 -0
- package/plugins/publishings/qiita-pub/package.json +2 -1
- package/plugins/publishings/qiita-pub/src/index.ts +9 -9
- package/plugins/publishings/surfdash-pub/package.json +2 -1
- package/plugins/publishings/surfdash-pub/src/index.ts +17 -12
- package/plugins/publishings/zenn-pub/package.json +2 -1
- package/plugins/publishings/zenn-pub/src/index.ts +11 -11
- package/plugins/kk/agents/judge-compliance.md +0 -37
- package/plugins/kk/agents/judge-tech.md +0 -35
- package/plugins/kk/agents/judge-tone.md +0 -37
- /package/plugins/kk/skills/{judge → content-judge}/references/rubrics.md +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# English-platform playbook (X / Medium / Substack)
|
|
2
|
+
|
|
3
|
+
Distilled from the STORM study "How to Write and Distribute IT Technical Documents on English
|
|
4
|
+
Platforms" (works dir `how-to-write-it-technical-documents-for-english-a6a76477`, provenance only).
|
|
5
|
+
Load this reference when `--playbook english`; it shapes platform choice, format, and distribution
|
|
6
|
+
for the draft — never reimplements publishing.
|
|
7
|
+
|
|
8
|
+
## Choose the platform before you write
|
|
9
|
+
|
|
10
|
+
- The writer's-first-question fallacy: pick a platform *after* deciding what you actually want as
|
|
11
|
+
a writer. Technical content is expensive to produce; making it for the wrong audience on the
|
|
12
|
+
wrong surface wastes your scarcest resource.
|
|
13
|
+
- Compare platforms on three axes:
|
|
14
|
+
- **Growth/discoverability** — Medium: internal distribution, but the algorithm decides reach.
|
|
15
|
+
X: reach with high volatility. Substack: slow to start, compounds (Notes, recommendations,
|
|
16
|
+
direct email) into an asset you keep.
|
|
17
|
+
- **Audience/culture** — X is fast and combative; Medium skews essay readers; Substack readers
|
|
18
|
+
opt in deliberately (roughly 85% email / 10% app on a typical post).
|
|
19
|
+
- **Business model** — Medium pays per member read; X monetization is thin for most; Substack is
|
|
20
|
+
free, you own your subscriber list, paid subs optional (10% cut only when someone pays).
|
|
21
|
+
- For IT writers the email list is the difference between building equity and renting attention.
|
|
22
|
+
Cross-posting to Medium is a fine low-effort hedge — just understand its mechanics first.
|
|
23
|
+
|
|
24
|
+
## Launch and grow (Substack)
|
|
25
|
+
|
|
26
|
+
- Substack is three layers: email newsletter, publication (archive/about/navigation), and
|
|
27
|
+
social/discovery (Notes, recommendations, search). Most writers only use layer one — that is why
|
|
28
|
+
they struggle to grow.
|
|
29
|
+
- Setup order: name the publication (clear topic promise or memorable brand — not your personal
|
|
30
|
+
name unless known); write an attention-grabbing bio (what you write, who it's for, credentials);
|
|
31
|
+
import any existing subscriber list; publish a first post before promoting.
|
|
32
|
+
- Cadence: a sustainable frequency — one good post per week beats one "perfect" post per month;
|
|
33
|
+
shipping gets you feedback to iterate. Open each issue with a clear thesis and keep it readable
|
|
34
|
+
in an email client.
|
|
35
|
+
- **Growth levers combine**: from zero, Notes daily + connecting with writers gets the first
|
|
36
|
+
50–100 subs; 100–500 layers in guest posts, collaborations, and recommendations (one publication
|
|
37
|
+
gained ~7,000 subs through recommendations alone).
|
|
38
|
+
- **Notes** is the "new Twitter for writers": short valuable insights, questions, daily presence,
|
|
39
|
+
genuine engagement (comments, restacks). Not random hot takes or growth-hacking. A 3-2-1 weekly
|
|
40
|
+
rhythm: three value notes, two engagement posts, one soft promotion.
|
|
41
|
+
- **Own the channel, rent the algorithm**: the email list is the asset that survives platform
|
|
42
|
+
changes; Notes/X/LinkedIn are top-of-funnel discovery — valuable, volatile, never the foundation.
|
|
43
|
+
|
|
44
|
+
## Position and voice
|
|
45
|
+
|
|
46
|
+
- **Author-first** (the newsletter builds an audience for *you*; topics range widely) vs
|
|
47
|
+
**topic-first** (the publication is the draw; the niche is the promise — "production Kubernetes
|
|
48
|
+
war stories"). IT content tends to capture niche intent faster topic-first; author-first builds a
|
|
49
|
+
brand that travels.
|
|
50
|
+
- **Voice is the durable moat**: people want the person behind the publication. When automation
|
|
51
|
+
repurposes content, preserve conversational voice and framework thinking — never "AI rewrites my
|
|
52
|
+
stuff," which yields generic output.
|
|
53
|
+
|
|
54
|
+
## Repurposing (one long-form → many assets)
|
|
55
|
+
|
|
56
|
+
- One newsletter becomes 5 LinkedIn posts + 10 Substack Notes + 3 X threads via a webhook →
|
|
57
|
+
extract → generate → deliver pipeline (Make.com + Firecrawl + OpenAI + Google Docs), cutting
|
|
58
|
+
hours of manual reformatting to ~30 minutes of polish.
|
|
59
|
+
- **Adapt per platform, never copy-paste**: LinkedIn wants declarative hooks + strategic line
|
|
60
|
+
breaks; Substack Notes wants ~200-character insights with bold + mic-drop endings; X threads
|
|
61
|
+
need conversational hooks with curiosity gaps between tweets.
|
|
62
|
+
- Dense IT material (architecture tradeoffs, debugging walkthroughs, benchmarks) must be re-cut
|
|
63
|
+
per surface. Long-form Substack/Medium carries the full technical depth and is the canonical
|
|
64
|
+
source everything links back to.
|
|
65
|
+
- **The 10–20% human polish pass is mandatory and non-delegable** — for technical material this
|
|
66
|
+
is where accuracy lives: verify every code snippet, command, flag, and version-specific claim.
|
|
67
|
+
|
|
68
|
+
## Applying this in `create`
|
|
69
|
+
|
|
70
|
+
Load this reference when `--playbook english`. It informs the draft's framing (audience, voice,
|
|
71
|
+
platform-native format) and the distribution plan, but the deliverable stays `topic`'s
|
|
72
|
+
`3-draft/draft-article.md` — this playbook never publishes and never writes a parallel artifact.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# WeChat Official Account playbook (specific-platform study)
|
|
2
|
+
|
|
3
|
+
Distilled from the STORM study "How to Write and Publish Technical Documents on WeChat Official
|
|
4
|
+
Accounts" (works dir `how-to-write-it-technical-documents-for-specific-d158f7c2`, provenance only).
|
|
5
|
+
Load this reference when `--playbook wechat`; it shapes the draft for WeChat's reading context and
|
|
6
|
+
compliance constraints — never reimplements registration, publishing, or the API.
|
|
7
|
+
|
|
8
|
+
## Why WeChat
|
|
9
|
+
|
|
10
|
+
- For Chinese academics, engineers, and technical decision-makers, WeChat Official Accounts
|
|
11
|
+
(微信公众号) are the primary delivery channel — researchers check it almost every waking hour.
|
|
12
|
+
- Authority bar: an expert should be able to name at least five public accounts on the topic they
|
|
13
|
+
follow. If your account would not make that list, you are not reaching the expert audience.
|
|
14
|
+
|
|
15
|
+
## Account type and constraints (the drafting context)
|
|
16
|
+
|
|
17
|
+
- **Subscription account (订阅号)**: up to 1 push/day, but folded into a "Subscriptions" folder —
|
|
18
|
+
best for high-cadence technical publishing. **Service account (服务号)**: 4 messages/month but
|
|
19
|
+
appears in the main chat list with push — best for low-cadence, high-importance announcements.
|
|
20
|
+
- **Enterprise accounts (企业号)** are for internal newsletters (mutual approval, no public reach).
|
|
21
|
+
- **Hard limits that shape content**: each message carries up to 8 articles; **published articles
|
|
22
|
+
cannot be deleted** — only modified with a correction notice. Errors are permanent, so
|
|
23
|
+
review-before-publish is mandatory.
|
|
24
|
+
- Registration is a Chinese-identity-bound process (QR-scan binding, Chinese ID/phone for admins,
|
|
25
|
+
ICP / Mini Program filings for hosted infrastructure). Foreign entities must work through a
|
|
26
|
+
Chinese partner. API access needs AppID/AppSecret (QR-verified) and an IP whitelist.
|
|
27
|
+
|
|
28
|
+
## Authoring for the WeChat reader
|
|
29
|
+
|
|
30
|
+
- **Markdown-first pipeline**: WeChat's rich-text editor cannot interpret Markdown. Write in
|
|
31
|
+
Markdown, convert to WeChat-compatible rich text (MD2WeChat / `lyricat/wechat-format`, or the
|
|
32
|
+
Obsidian `wechat-public-platform` plugin for one-command draft upload), then paste/upload.
|
|
33
|
+
Converters turn external links into footnotes (external links are not allowed in articles).
|
|
34
|
+
- **Format for mobile consumption** (articles are read on phones — commuting, mid-meeting):
|
|
35
|
+
short paragraphs, images/GIFs/audio/video (text-only underperforms), text-light images, abstract
|
|
36
|
+
(摘要) under 120 characters, 900×500px cover, Simplified Chinese, short self-explanatory titles
|
|
37
|
+
(truncated after ~26 Chinese characters), h2–h4 headings (leave h1 for larger embedding), 15px
|
|
38
|
+
body text, PingFang on iOS.
|
|
39
|
+
- **Language**: Chinese-language articles outperform English even for Chinese academics who read
|
|
40
|
+
English; some international accounts publish multilingual content.
|
|
41
|
+
- **What engages**: novelty (new findings/new resources), originality and authenticity, and
|
|
42
|
+
structural rigor — this audience is trained to be critical of a WeChat post.
|
|
43
|
+
|
|
44
|
+
## Editorial standards for authority
|
|
45
|
+
|
|
46
|
+
- **Bilingual/translated material**: the CAICT model — long-form Chinese analyses of international
|
|
47
|
+
technical reports, crediting Chinese-institution contributors — is the reference pattern.
|
|
48
|
+
- **Worth a "top five" slot**: recurring high-signal formats (standards announcements, seminar
|
|
49
|
+
notices, joint research, translated analyses) plus originality, authenticity, and rigor.
|
|
50
|
+
- **Review-before-publish checklist** (forced by the no-deletion rule): preview to your own WeChat
|
|
51
|
+
first; verify title length, abstract, cover, and footnote-converted links; confirm originality
|
|
52
|
+
declaration, tags, and comment settings manually in the draft box; route the broadcast through
|
|
53
|
+
the admin QR-code verification step.
|
|
54
|
+
|
|
55
|
+
## Applying this in `create`
|
|
56
|
+
|
|
57
|
+
Load this reference when `--playbook wechat`. It informs the draft's structure (mobile-first
|
|
58
|
+
formatting, Chinese-language expectations, title/abstract limits, review rigor) but the deliverable
|
|
59
|
+
stays `topic`'s `3-draft/draft-article.md` — this playbook never registers an account, never
|
|
60
|
+
publishes, and never touches the WeChat API.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Skill-authoring self-discipline (why this skill stays lean)
|
|
2
|
+
|
|
3
|
+
Distilled from the STORM study "How to Write the Best Technical Documentation with Agent Skills"
|
|
4
|
+
(works dir `how-to-write-the-best-technical-document-with-ex-2c52b35c`, provenance only). This
|
|
5
|
+
reference is the meta-discipline for *this skill*: it exists so `itc-generating` stays a thin
|
|
6
|
+
method+orchestration layer instead of re-vendoring craft or reimplementing sibling skills.
|
|
7
|
+
|
|
8
|
+
## The SKILL.md contract (hard limits)
|
|
9
|
+
|
|
10
|
+
- **Frontmatter**: `name` ≤64 chars, lowercase letters/numbers/hyphens only, no XML tags or
|
|
11
|
+
reserved words, and must exactly match the parent directory name. `description` is non-empty,
|
|
12
|
+
≤1,024 chars, no XML tags.
|
|
13
|
+
- **Body budget**: keep SKILL.md under ~500 lines / ~5,000 tokens — just the core instructions the
|
|
14
|
+
agent needs every run; split the rest into `references/`.
|
|
15
|
+
- **Context economics**: only `name` + `description` are pre-loaded; the rest enters context only
|
|
16
|
+
when needed. The description is the *only* routing signal — write it as a third-person capability
|
|
17
|
+
statement with explicit negative triggers so near-miss tasks are excluded.
|
|
18
|
+
- **Folder grammar**: SKILL.md + `references/` + `scripts/` + `assets/`, kept **exactly one level
|
|
19
|
+
deep** (`references/schema.md`, not `references/db/v1/schema.md`). Avoid nested reference chains —
|
|
20
|
+
when references point at references, the agent previews instead of reading fully.
|
|
21
|
+
|
|
22
|
+
## Progressive disclosure (the load-on-demand rule)
|
|
23
|
+
|
|
24
|
+
- Keep the always-loaded core minimal; push detail into `references/` with **just-in-time
|
|
25
|
+
pointers**: "Read `references/api-errors.md` if the API returns a non-200" beats "see
|
|
26
|
+
references/ for details".
|
|
27
|
+
- Three patterns: high-level guide + references; domain-specific organization; conditional details
|
|
28
|
+
(branch to a section only when a condition holds).
|
|
29
|
+
- Prefer **deterministic scripts** for fragile/repetitive operations (link checks, format
|
|
30
|
+
validation) — not for judgment-heavy authoring. Design their failure modes deliberately.
|
|
31
|
+
|
|
32
|
+
## Instruction design
|
|
33
|
+
|
|
34
|
+
- **Degree of freedom**: prescriptive exact-sequence instructions for fragile operations; goal-plus-
|
|
35
|
+
philosophy for judgment-heavy work (authoring). Vague skills fail — "handle errors appropriately"
|
|
36
|
+
carries no information.
|
|
37
|
+
- **Defaults, not menus**: pick one default and a one-line escape hatch, rather than listing
|
|
38
|
+
options and hoping the agent chooses well.
|
|
39
|
+
- **Add what the agent lacks; omit what it knows**: the filter is "would the agent get this wrong
|
|
40
|
+
without this instruction?" If no, cut it.
|
|
41
|
+
- Highest-value blocks: gotchas sections (environment facts that defy assumptions), output-format
|
|
42
|
+
templates, checklists and self-check loops.
|
|
43
|
+
|
|
44
|
+
## Evaluation and review (how to keep improving the skill)
|
|
45
|
+
|
|
46
|
+
- Four validation passes: discovery (description in isolation — does it route correctly?),
|
|
47
|
+
logic (deterministic steps, no hallucinated gaps), adversarial edge-case QA ("try to break this
|
|
48
|
+
skill"), architecture refinement (enforce progressive disclosure).
|
|
49
|
+
- **Execute-then-refine** beats endless pre-polish: run the skill on real tasks and iterate;
|
|
50
|
+
read execution traces, not just outputs (instructions retried many times, instructions followed
|
|
51
|
+
that don't apply, too many options without a default).
|
|
52
|
+
- Add a **self-reflection step**: after a run, examine the friction log and improve the skill so
|
|
53
|
+
the next run does not struggle the same way.
|
|
54
|
+
|
|
55
|
+
## How this skill applies it
|
|
56
|
+
|
|
57
|
+
`itc-generating` is one-level-deep by construction: SKILL.md points at the four `references/*.md`
|
|
58
|
+
files below it and loads them on demand (generic-craft always; playbook only when `--playbook` is
|
|
59
|
+
not `generic`). It reuses `topic`'s artifact contract rather than inventing a parallel workspace
|
|
60
|
+
(D5), delegates research/judging to `storm-research`/`content-judge` rather than reimplementing them (D6),
|
|
61
|
+
and stays under the body budget because the four STORM reports live in the works dirs as
|
|
62
|
+
provenance — never vendored here (D4).
|
|
@@ -35,7 +35,7 @@ Use when:
|
|
|
35
35
|
Do **not** use for:
|
|
36
36
|
|
|
37
37
|
- Authoring an article from a topic with no research need — that is `topic`.
|
|
38
|
-
- Quality gating / judging generated content — that is `judge`.
|
|
38
|
+
- Quality gating / judging generated content — that is `content-judge`.
|
|
39
39
|
- Publishing, platform adaptation, or illustration — out of C4 scope.
|
|
40
40
|
- Anything requiring a raw `kk executor run` chain when the workflow already composes it.
|
|
41
41
|
|
|
@@ -93,7 +93,7 @@ skill is also callable directly as `Skill(skill="storm-research", args="<topic|-
|
|
|
93
93
|
works dir (re-running a topic is legal — no `--force` needed).
|
|
94
94
|
- If `raw` derives empty (no H1 and an empty basename, e.g. a file named `.md`) → fail loud.
|
|
95
95
|
|
|
96
|
-
## Works layout (0056, frozen)
|
|
96
|
+
## Works layout (0056, frozen; ADR-013 adds `references/`)
|
|
97
97
|
|
|
98
98
|
Per-topic workspace at `$works_dir/<topic-id>/`:
|
|
99
99
|
|
|
@@ -105,6 +105,7 @@ Per-topic workspace at `$works_dir/<topic-id>/`:
|
|
|
105
105
|
| `docs.json` | ingest | `web-search` output `Doc[]` |
|
|
106
106
|
| `content.json` | generate | STORM `Content` (machine contract) |
|
|
107
107
|
| `content.md` | render | markdown sidecar report |
|
|
108
|
+
| `references/<doc-id>.md` | ingest (live path only) | normalized full content per source (`/v2/scrape` → markitdown); file name = Doc id; audit trail and the citation-resolution target for the report tail (ADR-013) |
|
|
108
109
|
|
|
109
110
|
Contrast: `topic`'s authoring workspace is `./<kebab-case(topic)>` with `brief.md` /
|
|
110
111
|
`2-outline/` / `3-draft/` and explicitly excludes research. C4 works live under `$works_dir`,
|
|
@@ -130,9 +131,10 @@ outside any repo.
|
|
|
130
131
|
| `--in` file unreadable (missing / I/O) | exit 1, stderr names the path | fix the path / permissions |
|
|
131
132
|
| Derived query / topic-id empty (no H1 and empty basename) | exit 1, stderr shows the derived value | add an H1 or give the file a real name |
|
|
132
133
|
| Dest `$workflows_dir/<name>.yaml` exists with different bytes | warning, **no overwrite**; run proceeds with the user's copy | re-run with `--force` to replace from the install source |
|
|
133
|
-
| Workflow YAML cannot be resolved from any install source | exit 1, stderr lists the roots searched | reinstall the package / set `KK_WORKFLOWS_SOURCE` |
|
|
134
|
+
| Workflow YAML cannot be resolved from any install source | exit 1, stderr lists the roots searched (`KK_WORKFLOWS_SOURCE`, `plugins/kk/workflows`) | reinstall the package / set `KK_WORKFLOWS_SOURCE` |
|
|
134
135
|
| `KK_CONFIG` set but unreadable, or config unparseable / invalid types | exit 1, stderr names the key + expected vs actual type | fix the file, or delete it (recreated with defaults next run) |
|
|
135
136
|
| Missing config at `KK_CONFIG` | **not a failure** — create-on-first-run with compiled defaults; an existing file is never overwritten | — |
|
|
137
|
+
| `markitdown` missing from `PATH` (live mode) | ingest exit 1, stderr `web-search failed: markitdown is not on PATH`; probed before any network call, so no `docs.json` write | install markitdown / put it on `PATH`, re-run ingest |
|
|
136
138
|
|
|
137
139
|
Every failure names the exact path and the exact next command. Never silently skip, never
|
|
138
140
|
auto-replace, never hash topic-id in the YAML.
|
|
@@ -154,6 +156,10 @@ auto-replace, never hash topic-id in the YAML.
|
|
|
154
156
|
6. **Prefer primary sources in the markdown input file.** When a source file is supplied via
|
|
155
157
|
`--in`, favor the primary material in it (first-party specs, owned notes, authoritative
|
|
156
158
|
extracts) as the spine of the report; web results fill gaps, not the reverse.
|
|
159
|
+
7. **Digest, don't index.** References are evidence, not the deliverable: the report synthesizes
|
|
160
|
+
findings across the supplied documents, compares them, and states conclusions — it never
|
|
161
|
+
lists sources one per line. The render drops the `## Outline` replay (ADR-013 D7); the
|
|
162
|
+
references tail plus `references/<doc-id>.md` files are the audit trail behind each cite id.
|
|
157
163
|
|
|
158
164
|
## Invariants
|
|
159
165
|
|
|
@@ -164,3 +170,4 @@ auto-replace, never hash topic-id in the YAML.
|
|
|
164
170
|
`kk` CLI verb.
|
|
165
171
|
- `FIRECRAWL_API_KEY` stays env-only.
|
|
166
172
|
- Works layout file names are frozen (0056) — do not rename or add sidecars in this skill.
|
|
173
|
+
ADR-013 exception: the ingest live path writes `references/<doc-id>.md` per cached source.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
2
|
+
kind: state-machine
|
|
3
|
+
name: judge-gated-publish
|
|
4
|
+
description: "Example workflow where an LLM-as-judge agent.run step gates a publish transition"
|
|
5
|
+
initialState: generate
|
|
6
|
+
terminalStates:
|
|
7
|
+
- done
|
|
8
|
+
- failed
|
|
9
|
+
failureStates:
|
|
10
|
+
- failed
|
|
11
|
+
vars:
|
|
12
|
+
contentFile: ".spur/run/content.json"
|
|
13
|
+
verdictFile: ".spur/run/verdict.json"
|
|
14
|
+
rubric: "general"
|
|
15
|
+
maxRetries: "2"
|
|
16
|
+
# attempt is mutated by the revise loop; initialize to 0.
|
|
17
|
+
attempt: "0"
|
|
18
|
+
spurBin: "spur"
|
|
19
|
+
states:
|
|
20
|
+
- id: generate
|
|
21
|
+
description: "Generate content from brief or docs (kk invoke <generator>)"
|
|
22
|
+
onEnter:
|
|
23
|
+
- kind: shell
|
|
24
|
+
options:
|
|
25
|
+
# Seed the verdict so the judge gate guards can be evaluated in --dry-run
|
|
26
|
+
# (dry-run skips actions but still evaluates shell guards). A real run's
|
|
27
|
+
# judge step overwrites this file before the gate is read.
|
|
28
|
+
command: "echo '{\"verdict\":\"PASS\",\"score\":0.0}' > ${vars.verdictFile} && echo 'generating content...'"
|
|
29
|
+
- id: judge
|
|
30
|
+
description: "Evaluate generated content via the judge skill (agent.run)"
|
|
31
|
+
onEnter:
|
|
32
|
+
# `agent.run` is registered by the kk workflow host: it spawns an agent that loads the
|
|
33
|
+
# content-judge skill, evaluates the content against ${vars.rubric}, and writes the verdict
|
|
34
|
+
# JSON to ${vars.verdictFile}. Under `spur workflow run` (dry-run), this action is
|
|
35
|
+
# skipped — the seeded verdict above keeps the gate evaluable. See
|
|
36
|
+
# plugins/kk/skills/content-judge/SKILL.md and references/workflow-integration.md.
|
|
37
|
+
- kind: agent.run
|
|
38
|
+
options:
|
|
39
|
+
role: reviewer
|
|
40
|
+
input: |
|
|
41
|
+
Evaluate the content at ${vars.contentFile} against the ${vars.rubric} rubric.
|
|
42
|
+
Load plugins/kk/skills/content-judge/SKILL.md and its references/rubrics.md.
|
|
43
|
+
Write the verdict JSON to ${vars.verdictFile} per the contract in SKILL.md.
|
|
44
|
+
Print the verdict path as the last line of stdout.
|
|
45
|
+
expectFile: ${vars.verdictFile}
|
|
46
|
+
- id: publish
|
|
47
|
+
description: "Publish content after clearing judge gate (kk fanout)"
|
|
48
|
+
onEnter:
|
|
49
|
+
- kind: shell
|
|
50
|
+
options:
|
|
51
|
+
command: "echo 'publishing content...'"
|
|
52
|
+
- id: done
|
|
53
|
+
description: "Publish completed successfully"
|
|
54
|
+
- id: failed
|
|
55
|
+
description: "Judge rejected content (FAIL) or retries exhausted (NEEDS_REVISION)"
|
|
56
|
+
|
|
57
|
+
transitions:
|
|
58
|
+
- from: generate
|
|
59
|
+
to: judge
|
|
60
|
+
description: "Content generated -> proceed to evaluation"
|
|
61
|
+
guard:
|
|
62
|
+
kind: always
|
|
63
|
+
|
|
64
|
+
# PASS -> publish.
|
|
65
|
+
- from: judge
|
|
66
|
+
to: publish
|
|
67
|
+
description: "Judge verdict is PASS (or file absent under --dry-run) -> proceed to publish"
|
|
68
|
+
guard:
|
|
69
|
+
kind: shell
|
|
70
|
+
options:
|
|
71
|
+
# Fail-closed on an explicit non-PASS verdict; fail-open only when the file is
|
|
72
|
+
# absent, which happens under --dry-run (actions are skipped, so the judge
|
|
73
|
+
# never writes its verdict). A real run always has the file.
|
|
74
|
+
command: 'v="$(jq -r .verdict ${vars.verdictFile} 2>/dev/null)"; test -z "$v" -o "$v" = PASS'
|
|
75
|
+
|
|
76
|
+
# NEEDS_REVISION -> back to generate with a bounded retry budget.
|
|
77
|
+
- from: judge
|
|
78
|
+
to: generate
|
|
79
|
+
description: "Judge verdict is NEEDS_REVISION and retry budget remains -> regenerate with feedback"
|
|
80
|
+
guard:
|
|
81
|
+
kind: shell
|
|
82
|
+
options:
|
|
83
|
+
# Allow the revise loop only while the attempt counter is below maxRetries.
|
|
84
|
+
# The shell kind cannot mutate vars, so the host (or a sidecar note action) is
|
|
85
|
+
# responsible for incrementing attempt on each loop in a real run.
|
|
86
|
+
command: 'v="$(jq -r .verdict ${vars.verdictFile} 2>/dev/null)"; test "$v" = NEEDS_REVISION -a "$attempt" -lt "$maxRetries"'
|
|
87
|
+
|
|
88
|
+
# FAIL, or NEEDS_REVISION with no retries left -> failed.
|
|
89
|
+
- from: judge
|
|
90
|
+
to: failed
|
|
91
|
+
description: "Judge verdict is FAIL, or NEEDS_REVISION with retries exhausted -> block publish"
|
|
92
|
+
guard:
|
|
93
|
+
kind: shell
|
|
94
|
+
options:
|
|
95
|
+
command: 'v="$(jq -r .verdict ${vars.verdictFile} 2>/dev/null)"; test -n "$v" && { test "$v" = FAIL || test "$v" = NEEDS_REVISION -a "$attempt" -ge "$maxRetries"; }'
|
|
96
|
+
|
|
97
|
+
- from: publish
|
|
98
|
+
to: done
|
|
99
|
+
description: "Publish done"
|
|
100
|
+
guard:
|
|
101
|
+
kind: always
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
---
|
|
2
|
+
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
3
|
+
name: kk-daily-ai-voice
|
|
4
|
+
kind: state-machine
|
|
5
|
+
description: >-
|
|
6
|
+
Daily AI news broadcast pipeline orchestrating aihot-ingest, dailynews-gen, and voice-gen
|
|
7
|
+
initialState: prepare
|
|
8
|
+
terminalStates:
|
|
9
|
+
- done
|
|
10
|
+
- failed
|
|
11
|
+
failureStates:
|
|
12
|
+
- failed
|
|
13
|
+
|
|
14
|
+
vars:
|
|
15
|
+
work_dir: ""
|
|
16
|
+
limit: "10"
|
|
17
|
+
language: "zh"
|
|
18
|
+
voice_profile: "Robin"
|
|
19
|
+
title: ""
|
|
20
|
+
cursor: ""
|
|
21
|
+
state_file: ""
|
|
22
|
+
skip_review: "false"
|
|
23
|
+
transcode_mp3: "false"
|
|
24
|
+
plugins_path: ""
|
|
25
|
+
__hitlAnswer: ""
|
|
26
|
+
|
|
27
|
+
states:
|
|
28
|
+
- id: prepare
|
|
29
|
+
description: "Create workspace subdirectories and validate parameters"
|
|
30
|
+
onEnter:
|
|
31
|
+
- kind: shell
|
|
32
|
+
options:
|
|
33
|
+
command: >-
|
|
34
|
+
test -n "${vars.work_dir}" || { echo "missing required variable: work_dir" >&2 && exit 1; };
|
|
35
|
+
mkdir -p "${vars.work_dir}" "${vars.work_dir}/1-ingest" "${vars.work_dir}/2-script" "${vars.work_dir}/3-audio";
|
|
36
|
+
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}"
|
|
37
|
+
|
|
38
|
+
- id: fetch
|
|
39
|
+
description: "Ingest latest curated AI news from aihot.virxact.com"
|
|
40
|
+
onEnter:
|
|
41
|
+
- kind: shell
|
|
42
|
+
options:
|
|
43
|
+
command: >-
|
|
44
|
+
set --;
|
|
45
|
+
if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
|
|
46
|
+
kk executor run aihot-ingest --in "${vars.work_dir}/1-ingest/source.json" --out "${vars.work_dir}/1-ingest/docs.json" "$@"
|
|
47
|
+
|
|
48
|
+
- id: script
|
|
49
|
+
description: "Compile news Doc[] into structured broadcast VoiceScript YAML Content"
|
|
50
|
+
onEnter:
|
|
51
|
+
- kind: shell
|
|
52
|
+
options:
|
|
53
|
+
command: >-
|
|
54
|
+
set --;
|
|
55
|
+
if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
|
|
56
|
+
DAILYNEWS_LANGUAGE="${vars.language}" DAILYNEWS_TITLE="${vars.title}" VOICEBOX_DEFAULT_PROFILE="${vars.voice_profile}" kk executor run dailynews-gen --in "${vars.work_dir}/1-ingest/docs.json" --out "${vars.work_dir}/2-script/content.json" "$@" &&
|
|
57
|
+
bun -e 'await Bun.write(process.argv[2], (await Bun.file(process.argv[1]).json()).body)' "${vars.work_dir}/2-script/content.json" "${vars.work_dir}/2-script/voicescript.yaml"
|
|
58
|
+
|
|
59
|
+
- id: review
|
|
60
|
+
description: "Operator reviews the generated news script before speech synthesis"
|
|
61
|
+
pause: true
|
|
62
|
+
onEnter:
|
|
63
|
+
- kind: hitl.confirm
|
|
64
|
+
options:
|
|
65
|
+
prompt: "Review daily AI news VoiceScript at ${vars.work_dir}/2-script/voicescript.yaml. Proceed to voice generation?"
|
|
66
|
+
var: __hitlAnswer
|
|
67
|
+
|
|
68
|
+
- id: wrap-docs
|
|
69
|
+
description: "Wrap VoiceScript into Doc[] input for voice-gen"
|
|
70
|
+
onEnter:
|
|
71
|
+
- kind: shell
|
|
72
|
+
options:
|
|
73
|
+
command: >-
|
|
74
|
+
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" } }; await Bun.write(process.argv[2], JSON.stringify([doc], null, 2))' "${vars.work_dir}/2-script/voicescript.yaml" "${vars.work_dir}/3-audio/docs.json" "${vars.voice_profile}"
|
|
75
|
+
|
|
76
|
+
- id: generate
|
|
77
|
+
description: "Invoke voice-gen generator plugin to generate audio"
|
|
78
|
+
onEnter:
|
|
79
|
+
- kind: shell
|
|
80
|
+
options:
|
|
81
|
+
command: >-
|
|
82
|
+
set --;
|
|
83
|
+
if [ -n "${vars.plugins_path}" ]; then set -- --plugins-path "${vars.plugins_path}"; fi;
|
|
84
|
+
VOICE_GEN_MP3="${vars.transcode_mp3}" kk executor run voice-gen --in "${vars.work_dir}/3-audio/docs.json" --out "${vars.work_dir}/3-audio/content.json" "$@"
|
|
85
|
+
|
|
86
|
+
- id: done
|
|
87
|
+
description: "Terminal — Daily AI news voice broadcast generated successfully"
|
|
88
|
+
- id: failed
|
|
89
|
+
description: "Terminal — Pipeline failed"
|
|
90
|
+
|
|
91
|
+
transitions:
|
|
92
|
+
- from: prepare
|
|
93
|
+
to: fetch
|
|
94
|
+
description: "Workspace prepared -> fetch news items"
|
|
95
|
+
guard:
|
|
96
|
+
kind: always
|
|
97
|
+
|
|
98
|
+
- from: fetch
|
|
99
|
+
to: script
|
|
100
|
+
description: "News ingested -> build broadcast script"
|
|
101
|
+
guard:
|
|
102
|
+
kind: always
|
|
103
|
+
|
|
104
|
+
- from: script
|
|
105
|
+
to: wrap-docs
|
|
106
|
+
description: "skip_review enabled -> bypass HITL review"
|
|
107
|
+
guard:
|
|
108
|
+
kind: shell
|
|
109
|
+
options:
|
|
110
|
+
command: 'test "${vars.skip_review}" = "true"'
|
|
111
|
+
|
|
112
|
+
- from: script
|
|
113
|
+
to: review
|
|
114
|
+
description: "skip_review disabled -> pause for operator review"
|
|
115
|
+
guard:
|
|
116
|
+
kind: always
|
|
117
|
+
|
|
118
|
+
- from: review
|
|
119
|
+
to: wrap-docs
|
|
120
|
+
description: "VoiceScript approved -> wrap for audio generation"
|
|
121
|
+
guard:
|
|
122
|
+
kind: shell
|
|
123
|
+
options:
|
|
124
|
+
command: 'test "${vars.__hitlAnswer}" = "yes"'
|
|
125
|
+
|
|
126
|
+
- from: review
|
|
127
|
+
to: failed
|
|
128
|
+
description: "VoiceScript rejected -> abort"
|
|
129
|
+
guard:
|
|
130
|
+
kind: shell
|
|
131
|
+
options:
|
|
132
|
+
command: 'test "${vars.__hitlAnswer}" = "no"'
|
|
133
|
+
|
|
134
|
+
- from: wrap-docs
|
|
135
|
+
to: generate
|
|
136
|
+
description: "Doc[] wrapped -> synthesize audio"
|
|
137
|
+
guard:
|
|
138
|
+
kind: always
|
|
139
|
+
|
|
140
|
+
- from: generate
|
|
141
|
+
to: done
|
|
142
|
+
description: "Audio generated successfully -> complete"
|
|
143
|
+
guard:
|
|
144
|
+
kind: always
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Linear state-machine workflow template for knowledge-kit plugin platform:
|
|
2
|
+
# ingest -> generate -> publish -> done.
|
|
3
|
+
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
4
|
+
name: kk-ingest-generate-publish
|
|
5
|
+
kind: state-machine
|
|
6
|
+
description: Chained ingestion, generator, and publish plugins via kk executor run
|
|
7
|
+
initialState: ingest
|
|
8
|
+
terminalStates:
|
|
9
|
+
- done
|
|
10
|
+
- failed
|
|
11
|
+
vars:
|
|
12
|
+
ingestion_plugin: "karakeep-local"
|
|
13
|
+
generator_plugin: "content-gen"
|
|
14
|
+
publish_plugin: "surfdash-pub"
|
|
15
|
+
source: "./source"
|
|
16
|
+
work_dir: "./work"
|
|
17
|
+
plugins_path: "./plugins"
|
|
18
|
+
|
|
19
|
+
states:
|
|
20
|
+
- id: ingest
|
|
21
|
+
description: Run ingestion plugin to produce document list (docs.json)
|
|
22
|
+
onEnter:
|
|
23
|
+
- kind: shell
|
|
24
|
+
options:
|
|
25
|
+
command: kk executor run ${vars.ingestion_plugin} --in ${vars.source} --out ${vars.work_dir}/docs.json --plugins-path ${vars.plugins_path}
|
|
26
|
+
|
|
27
|
+
- id: generate
|
|
28
|
+
description: Run content generator plugin to produce markdown content (content.json)
|
|
29
|
+
onEnter:
|
|
30
|
+
- kind: shell
|
|
31
|
+
options:
|
|
32
|
+
command: kk executor run ${vars.generator_plugin} --in ${vars.work_dir}/docs.json --out ${vars.work_dir}/content.json --plugins-path ${vars.plugins_path}
|
|
33
|
+
|
|
34
|
+
- id: publish
|
|
35
|
+
description: Run publish plugin to deploy content and receive result (result.json)
|
|
36
|
+
onEnter:
|
|
37
|
+
- kind: shell
|
|
38
|
+
options:
|
|
39
|
+
command: kk executor run ${vars.publish_plugin} --in ${vars.work_dir}/content.json --out ${vars.work_dir}/result.json --plugins-path ${vars.plugins_path}
|
|
40
|
+
|
|
41
|
+
- id: done
|
|
42
|
+
description: Terminal — all three plugin steps succeeded
|
|
43
|
+
- id: failed
|
|
44
|
+
description: Terminal — one of the plugin run steps failed
|
|
45
|
+
|
|
46
|
+
transitions:
|
|
47
|
+
- from: ingest
|
|
48
|
+
to: generate
|
|
49
|
+
description: Ingest succeeded — proceed to content generation
|
|
50
|
+
guard:
|
|
51
|
+
kind: action-ok
|
|
52
|
+
- from: ingest
|
|
53
|
+
to: failed
|
|
54
|
+
description: Ingest failed — terminate workflow
|
|
55
|
+
|
|
56
|
+
- from: generate
|
|
57
|
+
to: publish
|
|
58
|
+
description: Content generation succeeded — proceed to publish
|
|
59
|
+
guard:
|
|
60
|
+
kind: action-ok
|
|
61
|
+
- from: generate
|
|
62
|
+
to: failed
|
|
63
|
+
description: Generator failed — terminate workflow
|
|
64
|
+
|
|
65
|
+
- from: publish
|
|
66
|
+
to: done
|
|
67
|
+
description: Publish succeeded — workflow complete
|
|
68
|
+
guard:
|
|
69
|
+
kind: action-ok
|
|
70
|
+
- from: publish
|
|
71
|
+
to: failed
|
|
72
|
+
description: Publish failed — terminate workflow
|