From 7a582bac39649e395009bba3679e2f70039ab72d Mon Sep 17 00:00:00 2001 From: Duyet Le Date: Sun, 14 Jun 2026 01:33:21 +0700 Subject: [PATCH 1/3] feat(kb): add kb plugin with dream consolidation skill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dream skill: consolidate a markdown KB — merge near-duplicates, flag contradictions, prune stale notes, ingest inbox, refresh from sources, split, rebuild the index. Diff-for-approval; --auto (lock-guarded, skips conflicts); forget (delete by query). KB-agnostic (path arg / $KB_DIR). - Registered in marketplace.json, .claude-plugin/marketplace.json, .agents/plugins/marketplace.json; Claude + Codex manifests in sync. Co-Authored-By: duyetbot --- .agents/plugins/marketplace.json | 12 ++ .claude-plugin/marketplace.json | 5 + kb/.claude-plugin/plugin.json | 12 ++ kb/.codex-plugin/plugin.json | 19 +++ kb/README.md | 30 +++++ kb/skills/dream/SKILL.md | 196 +++++++++++++++++++++++++++++++ marketplace.json | 8 ++ 7 files changed, 282 insertions(+) create mode 100644 kb/.claude-plugin/plugin.json create mode 100644 kb/.codex-plugin/plugin.json create mode 100644 kb/README.md create mode 100644 kb/skills/dream/SKILL.md diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index a6f32c0..e47e687 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -255,6 +255,18 @@ "authentication": "ON_INSTALL" }, "category": "Automation" + }, + { + "name": "kb", + "source": { + "source": "local", + "path": "./kb" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Productivity" } ] } diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index e08c1e0..ef285ac 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -103,6 +103,11 @@ "name": "agent-loop", "source": "./agent-loop", "description": "Continuous agent loop for overnight/day repo maintenance. Triage, autoreview, browser automation, and parallel agent work. Wake every 5 min, dispatch to threads, land autonomously." + }, + { + "name": "kb", + "source": "./kb", + "description": "Knowledge base maintenance skills. Consolidate, dedupe, prune, refresh, and forget notes in a markdown KB." } ] } diff --git a/kb/.claude-plugin/plugin.json b/kb/.claude-plugin/plugin.json new file mode 100644 index 0000000..b2f9921 --- /dev/null +++ b/kb/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "kb", + "version": "1.0.0", + "description": "Knowledge base maintenance skills. Consolidate, dedupe, prune, refresh, and forget notes in a markdown KB.", + "author": { + "name": "duyet", + "url": "https://github.com/duyet" + }, + "homepage": "https://github.com/duyet/codex-claude-plugins", + "repository": "https://github.com/duyet/codex-claude-plugins", + "license": "MIT" +} diff --git a/kb/.codex-plugin/plugin.json b/kb/.codex-plugin/plugin.json new file mode 100644 index 0000000..f5ec3f0 --- /dev/null +++ b/kb/.codex-plugin/plugin.json @@ -0,0 +1,19 @@ +{ + "name": "kb", + "version": "1.0.0", + "description": "Knowledge base maintenance skills. Consolidate, dedupe, prune, refresh, and forget notes in a markdown KB.", + "author": { + "name": "duyet" + }, + "skills": "./skills/", + "interface": { + "displayName": "Knowledge Base", + "shortDescription": "Maintain a markdown knowledge base: consolidate, dedupe, prune, and forget notes.", + "developerName": "duyet", + "category": "productivity", + "capabilities": ["Skill"], + "links": { + "homepage": "https://github.com/duyet/codex-claude-plugins" + } + } +} diff --git a/kb/README.md b/kb/README.md new file mode 100644 index 0000000..8f4629a --- /dev/null +++ b/kb/README.md @@ -0,0 +1,30 @@ +# kb + +Knowledge base maintenance skills for markdown KBs. KB-agnostic: point each skill at any +markdown knowledge base via a path argument or the `$KB_DIR` environment variable. + +## Skills + +- **dream** — the consolidation pass. Merges near-duplicate notes, flags contradictions, + prunes stale/low-value notes, ingests inbox captures, refreshes notes from their + source URLs, splits multi-fact notes, relinks/retags, and rebuilds the index. Shows a + diff for approval before changing anything. Supports `--auto` (non-interactive, lock + guarded) and `forget` (delete specific notes). + +## Install + +```bash +/plugin install kb@duyet-claude-plugins +``` + +## Usage + +```bash +dream ./docs/kb # consolidate a KB (interactive, diff-for-approval) +dream $KB_DIR --auto # non-interactive; merges/prunes, skips contradictions +dream forget "old server" # propose deleting notes matching a query +``` + +The KB is any directory of `*.md` notes with YAML frontmatter (`name`, `type`, `tags`, +`created`, `updated`, optional `pinned`/`confidence`/`sources`), optionally with a +`MEMORY.md` index, a `raw/inbox/` of captures, and `raw/` source docs. diff --git a/kb/skills/dream/SKILL.md b/kb/skills/dream/SKILL.md new file mode 100644 index 0000000..8280c75 --- /dev/null +++ b/kb/skills/dream/SKILL.md @@ -0,0 +1,196 @@ +--- +name: dream +description: Consolidate a markdown knowledge base — merge near-duplicates, flag contradictions, prune stale notes, ingest inbox, refresh from sources, split, and rebuild the index. Use when a KB is noisy or stale, on a schedule, or to forget notes. Diff-for-approval; supports --auto. +--- + +# dream — KB memory consolidation + +## Overview + +Dream is the maintenance pass that keeps a markdown knowledge base compact and +retrievable — the agent equivalent of sleep consolidating the day's memories. Run it when +the KB feels noisy (duplicates, contradictions, sprawl, stale facts), on a schedule, or +to forget specific notes. Everything is proposed as a diff first; nothing changes until +you approve (or you run `--auto`). + +Dream is KB-agnostic and generic. It never assumes a fixed location. + +## Invocation + +- `dream []` — consolidate the KB at ``. If omitted, use `$KB_DIR`; if that + is unset, ask for the path. Examples: `dream ./docs/kb`, `dream $KB_DIR`. +- `dream --auto []` — non-interactive: apply merges and prunes silently, skip + contradictions (they need human judgment), guarded by a lock file. +- `dream forget "" []` — find notes matching a query and propose deletion + (same diff-for-approval flow as consolidation). + +A note is any `*.md` file with YAML frontmatter. Map missing fields gracefully: + +```yaml +--- +name: type-short-slug # equals the filename stem +description: one line, keyword-front-loaded +type: tech # user | feedback | project | reference | tech | ... +tags: [topic, topic] +sources: ["https://example.com/llms.txt"] +created: 2026-06-01 +updated: 2026-06-13 +pinned: false # optional — pinned notes are never pruned or merged +confidence: 0.8 # optional — 0..1; low + generic => prune candidate +--- +``` + +- No `type` => `unknown`. No `created`/`updated` => use file mtime. No `confidence` => + neutral. No `pinned` => false. + +## The pass (run strictly in order) + +Work in memory until the apply step; modify nothing early. + +1. **Resolve the KB.** Confirm `` holds `memory/` (notes) and ideally `MEMORY.md` + (index), `raw/inbox/` (captures), `raw/` (source docs), and a state file. If the + layout differs, operate on whatever `*.md` files exist and adapt. +2. **Load.** Read the index, every note, `raw/inbox/*`, source docs, and state. +3. **Ingest inbox.** Promote durable, general facts from `raw/inbox/*` into `memory/` + (create or merge notes). Delete inbox files once distilled (they are ephemeral). Keep + `raw/` source docs (immutable ground truth); record processed ones in state. +4. **Dedupe and merge (near-duplicates).** Group notes by `type`. Two notes are + near-duplicates when: same `type`; significant-noun overlap above ~60% (a proxy for + cosine similarity > 0.9); neither is `pinned`. Draft a merged note that is more + complete and specific than either, keeping the clearest, most recent phrasing. + Lossless of meaning, not of words. +5. **Contradictions.** Two notes assert opposing facts on the same topic (for example + "deploy to ECS" vs "deploy to Vercel"). Proposed winner: more recent with higher + confidence. Present both for A/B/skip (interactive); skip in `--auto`. +6. **Split.** If a note holds multiple distinct facts, split into atomic notes (one fact + per file), renamed to `-.md`. +7. **Validate frontmatter.** Every note has `name` (equals filename stem), `description`, + `type`, `tags`, `created`, `updated`. Fix the `-` filename prefix if the type + changed. No nested `metadata:` blocks. +8. **Refresh from sources.** For notes with a `sources:` URL whose `updated:` is stale + (older than ~30 days) or whose facts look outdated, fetch the source (prefer + `llms.txt`), update the note, bump `updated:`. Skip silently when offline. +9. **Compact.** Trim each note to essential facts; drop filler. Aim for ~25 lines or + fewer. +10. **Prune candidates.** A note is a prune candidate when any holds: + - its `type` has a retention policy and it is older than the policy's days (compare + `created`/`updated` to today); + - `confidence` is below 0.3 AND it has no project-unique info (no paths, + identifiers, or domain nouns); + - it is wrong, obsolete, or out of scope. + Never prune `pinned: true`. Prefer refreshing (step 8) over deleting merely because + a note is old. +11. **Relink and retag.** Fix `[[slug]]` references; ensure no orphans (each note links + at least one other); merge tag sprawl into the controlled vocabulary; drop links to + deleted notes. +12. **Rebuild index.** Regenerate `MEMORY.md` from surviving notes, grouped by `type`, + one line each: `[Title](file) — hook`. +13. **Verify scope.** Confirm no secrets, hosts, or confidential facts slipped in; + remove any that did. +14. **Stamp.** Set `state.last_dream` to today. + +## Retention policies (load in step 1) + +Default per-type retention, in days (`null` means keep forever). Override via a +`retention:` map in the KB's state or config file. + +- `feedback`: 180 +- `project`: null +- `reference`: null +- `tech`: 365 +- `user`: null +- `unknown`: 90 + +## Diff report (interactive) + +Print exactly this before any change. Omit any section that has zero items. + +``` +## dream — consolidation report + +Merges (): + [note:] + [note:] -> "" + +Conflicts (): + [note:] vs [note:] — "" [A/B/skip] + +Prune (): + [note:] — , d old + +Inbox ingested (), refreshed (), split (), relinked (). + +Proposed: merges, prunes, conflicts. Apply? [Y/n] +``` + +If there are zero proposals, print `Dream complete. No duplicate, contradictory, or stale +notes found.` and stop. + +## Resolve and apply (interactive) + +- For each conflict, collect `A`, `B`, or `skip` (empty input = skip). +- Final confirm: `Apply? [Y/n]`. `n` or `no` prints `Cancelled. No changes made.` and + stops. +- On confirm, apply in this order: + 1. **Merges** — write the merged note (`-.md`; `type` and `updated` = today; + `confidence` = the higher of the two originals; a `source: dream` marker), delete + both originals, and rewrite inbound links to point at the merged slug. + 2. **Contradictions** — delete the loser of each resolved A/B pair; skipped pairs stay + untouched. + 3. **Prunes** — delete the note and its index line. + 4. **Split, refresh, compact, relink, index rebuild** — apply the file writes. + 5. **Stamp** state, and run the KB's sync step only if one exists and only after the + user confirms — never auto-push. + +## Auto mode (`--auto`) + +- **Concurrency guard.** Before working, check `${TMPDIR:-/tmp}/dream_auto.lock`. If it + exists and is less than 10 minutes old, print + `[dream --auto] another run in progress — skipping.` and stop. Otherwise write the lock + (current timestamp); delete it on every exit path. +- Run load and analyze (steps 1–3). Apply merges and prunes silently (no diff, no + prompt). Skip contradictions (human judgment). Apply split, refresh, compact, relink, + index rebuild, and stamp. +- Print: `[dream --auto] kb= merged= pruned= conflicts_skipped=`. +- If contradictions were skipped, leave a single reminder note so the human runs + interactive `dream` to resolve them. First search for an existing `dream-auto` reminder + (by `source: dream-auto`); skip storing if one already exists. + +## forget + +`dream forget "" []` searches notes by keyword and semantic match, lists +them, and proposes deletion as a diff: + +``` +[note:] — "" [Y/n] +``` + +Apply only approved deletions (and their index lines). Pinned notes are listed but not +deleted unless explicitly confirmed per note. + +## Principles + +- **Lossless of meaning** — compaction removes words and redundancy, never facts. +- **Atomic** — one fact per file after the pass. +- **Retrieval-first** — every `description` lets an agent judge relevance from the index + alone; every note is reachable via tags and `[[links]]`. +- **Conservative deletes** — prune only what is wrong, redundant, or out of scope; refresh + rather than delete just because old. +- **Idempotent** — running twice in a row changes nothing the second time. +- **No surprises** — diff before write; never auto-push or auto-sync without approval. + +## Checklist + +- [ ] Resolved KB path from the argument or `$KB_DIR`; never assumed a fixed location. +- [ ] Held everything in memory; no writes before the diff. +- [ ] Inbox ingested and deleted; source docs kept. +- [ ] Near-duplicates merged (same type, >60% noun overlap, not pinned). +- [ ] Contradictions resolved (A/B) or skipped (`--auto`). +- [ ] Stale notes refreshed from sources before pruning; pinned notes untouched. +- [ ] Index rebuilt; no orphans; no broken `[[links]]`. +- [ ] Scope verified (no secrets, hosts, or confidential facts). +- [ ] State stamped; lock removed (`--auto`). + +## Resources + +- If the KB ships its own `DREAM.md` or `AGENTS.md`, read it first and align with its note + format, controlled vocabulary, and sync steps. diff --git a/marketplace.json b/marketplace.json index 867e964..fd75bb5 100644 --- a/marketplace.json +++ b/marketplace.json @@ -178,6 +178,14 @@ "version": "0.2.0", "type": "skill", "category": "automation" + }, + { + "name": "kb", + "id": "kb@duyet-claude-plugins", + "description": "Knowledge base maintenance skills. Consolidate, dedupe, prune, refresh, and forget notes in a markdown KB.", + "version": "1.0.0", + "type": "skill", + "category": "productivity" } ], "metadata": { From 588b69d265b5844a3ba62ad74246c55883e44699 Mon Sep 17 00:00:00 2001 From: Duyet Le <me@duyet.net> Date: Sun, 14 Jun 2026 01:36:44 +0700 Subject: [PATCH 2/3] feat(kb): add /dream command wrapping the dream skill Thin slash-command entry point: parses $ARGUMENTS (path / --auto / forget), resolves the KB via the argument or $KB_DIR, and delegates the full consolidation pass to the dream skill. Declares commands/ + Command capability in the Codex manifest. Co-Authored-By: duyetbot <duyetbot@users.noreply.github.com> --- kb/.codex-plugin/plugin.json | 3 ++- kb/commands/dream.md | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 kb/commands/dream.md diff --git a/kb/.codex-plugin/plugin.json b/kb/.codex-plugin/plugin.json index f5ec3f0..a8675e4 100644 --- a/kb/.codex-plugin/plugin.json +++ b/kb/.codex-plugin/plugin.json @@ -6,12 +6,13 @@ "name": "duyet" }, "skills": "./skills/", + "commands": "./commands/", "interface": { "displayName": "Knowledge Base", "shortDescription": "Maintain a markdown knowledge base: consolidate, dedupe, prune, and forget notes.", "developerName": "duyet", "category": "productivity", - "capabilities": ["Skill"], + "capabilities": ["Skill", "Command"], "links": { "homepage": "https://github.com/duyet/codex-claude-plugins" } diff --git a/kb/commands/dream.md b/kb/commands/dream.md new file mode 100644 index 0000000..05f0420 --- /dev/null +++ b/kb/commands/dream.md @@ -0,0 +1,34 @@ +# /dream + +Consolidate a markdown knowledge base: merge near-duplicates, flag contradictions, prune +stale notes, ingest inbox captures, refresh notes from sources, split, and rebuild the +index. Delegate to the `dream` skill for the full pass. Nothing changes until the user +approves the diff (unless `--auto`). + +## Arguments + +- `path`: KB directory (optional; defaults to `$KB_DIR`; ask if unset) +- `--auto`: non-interactive run (apply merges/prunes, skip contradictions, lock-guarded) +- `forget "<query>"`: delete notes matching a query (diff-for-approval) + +Pass the rest of the line as `$ARGUMENTS`. Examples: `/dream ./docs/kb`, +`/dream $KB_DIR --auto`, `/dream forget "old server"`. + +## Workflow + +1. Parse `$ARGUMENTS`: detect `--auto`, a `forget "<query>"` subcommand, and a path. + Resolve the KB path from the argument or `$KB_DIR`; ask if neither is set. +2. Invoke the `dream` skill and follow its pass exactly. The skill owns the consolidation + logic, the diff format, the apply order, and the safety rules. +3. In interactive mode, print the diff, collect A/B/skip for each conflict, confirm, then + apply in the skill's prescribed order. +4. In `--auto`, honor the lock file, apply merges and prunes silently, skip conflicts, and + leave a reminder if conflicts still need review. +5. For `forget`, list matches and apply only approved deletions (and their index lines). + +## Guardrails + +- Never assume a fixed KB location; use the argument or `$KB_DIR`. +- Never auto-push or auto-sync; the user confirms before any write outside the KB. +- Never delete or merge `pinned` notes without explicit per-note confirmation. +- Keep this command thin — all consolidation logic lives in the `dream` skill. From f4ce187797869e2ea01101bcae035038c14854b0 Mon Sep 17 00:00:00 2001 From: Duyet Le <me@duyet.net> Date: Sun, 14 Jun 2026 01:55:54 +0700 Subject: [PATCH 3/3] fix(kb): add languages to fenced code blocks (MD040) Super-linter markdownlint MD040 requires a language on fenced code blocks. The diff-report and forget-diff templates were bare; mark them as text. Co-Authored-By: duyetbot <duyetbot@users.noreply.github.com> --- kb/skills/dream/SKILL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kb/skills/dream/SKILL.md b/kb/skills/dream/SKILL.md index 8280c75..dc0e24a 100644 --- a/kb/skills/dream/SKILL.md +++ b/kb/skills/dream/SKILL.md @@ -105,7 +105,7 @@ Default per-type retention, in days (`null` means keep forever). Override via a Print exactly this before any change. Omit any section that has zero items. -``` +```text ## dream — consolidation report Merges (<N>): @@ -160,7 +160,7 @@ notes found.` and stop. `dream forget "<query>" [<path>]` searches notes by keyword and semantic match, lists them, and proposes deletion as a diff: -``` +```text [note:<file>] — "<title>" [Y/n] ```