feat(skills)!: rename the skills category to skill

kvb-skills-dream, -tune, -auto, -essence and -transcend become kvb-skill-*, and tests/test_naming.py now requires the category skill. enable-plugins.js registers the reminder hooks under the new names and repoints hook commands of the previous kvb-skills-* paths as well as the older autotune/dream paths; test_dream covers both generations. References in kvb-check-confidence, kvb-org-perform, kvb-org-rasci, kvb-transcend, the tests and the README organigram and skill map follow. The header comments of both reminder hooks and enable-plugins.js are dropped.

BREAKING CHANGE: /kvb-skills-dream, /kvb-skills-tune, /kvb-skills-auto, /kvb-skills-essence and /kvb-skills-transcend are now /kvb-skill-*.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kevin Veen-BirkenbachandClaude Opus 5.5 committed 2026-11-03 13:57:14 +01:00
1 parent 8be453b361
commit 359ea0cba0
17 files changed
+85 -95

No files matched your search

+18 -18
View File
@@ -9,7 +9,7 @@ Compatible with Claude Code, Codex, Gemini CLI, Cursor, Copilot, Windsurf, Cline
## Naming 🏷️
Every first-party skill is named `kvb-<category>-<name>` with one of these categories: `think`, `create`, `code`, `ci`, `git`, `check`, `org`, `skills`. Only `kvb-cleanup`, `kvb-help` and the collector `kvb-transcend` stand without a category. Names use `a-z`, `0-9` and single hyphens, at most 64 characters, and equal their directory name. `tests/test_naming.py` enforces all of it, including that the organigram below lists every skill under its category.
Every first-party skill is named `kvb-<category>-<name>` with one of these categories: `think`, `create`, `code`, `ci`, `git`, `check`, `org`, `skill`. Only `kvb-cleanup`, `kvb-help` and the collector `kvb-transcend` stand without a category. Names use `a-z`, `0-9` and single hyphens, at most 64 characters, and equal their directory name. `tests/test_naming.py` enforces all of it, including that the organigram below lists every skill under its category.
## Organigram 🏛️
@@ -25,7 +25,7 @@ flowchart LR
kvb --> git[git]
kvb --> check[check]
kvb --> org[org]
kvb --> skills[skills]
kvb --> skill[skill]
kvb --> none[no category]
think --> think_listen[kvb-think-listen]
think --> think_explain[kvb-think-explain]
@@ -64,11 +64,11 @@ flowchart LR
org --> org_artefacts[kvb-org-artefacts]
org --> org_wsjf[kvb-org-wsjf]
org --> org_robot[kvb-org-robot]
skills --> skills_dream[kvb-skills-dream]
skills --> skills_tune[kvb-skills-tune]
skills --> skills_auto[kvb-skills-auto]
skills --> skills_essence[kvb-skills-essence]
skills --> skills_transcend[kvb-skills-transcend]
skill --> skill_dream[kvb-skill-dream]
skill --> skill_tune[kvb-skill-tune]
skill --> skill_auto[kvb-skill-auto]
skill --> skill_essence[kvb-skill-essence]
skill --> skill_transcend[kvb-skill-transcend]
none --> cleanup[kvb-cleanup]
none --> help[kvb-help]
none --> transcend_all[kvb-transcend]
@@ -139,12 +139,12 @@ flowchart LR
wsjf[kvb-org-wsjf]
robot[kvb-org-robot]
end
subgraph cat_skills [skills]
dream[kvb-skills-dream]
autotune[kvb-skills-tune]
autoskill[kvb-skills-auto]
essence[kvb-skills-essence]
skills_transcend[kvb-skills-transcend]
subgraph cat_skill [skill]
dream[kvb-skill-dream]
autotune[kvb-skill-tune]
autoskill[kvb-skill-auto]
essence[kvb-skill-essence]
skill_transcend[kvb-skill-transcend]
end
subgraph cat_none [no category]
cleanup[kvb-cleanup]
@@ -225,12 +225,12 @@ flowchart LR
autoskill --> autotune
autotune --> essence
dream --> transcend
transcend --> skills_transcend
transcend --> skill_transcend
transcend --> code_transcend
skills_transcend --> active_listening
skills_transcend --> autotune
skill_transcend --> active_listening
skill_transcend --> autotune
code_transcend --> active_listening
autotune -. own scripts .-> skills_transcend
autotune -. own scripts .-> skill_transcend
tune -. measurements .-> code_transcend
test --> triage
@@ -259,7 +259,7 @@ Copy the skills into a specific project (`<repo>/.agents/skills` and `<repo>/.cl
make project TARGET=/path/to/repo
```
Both commands also patch the target's `.claude/settings.local.json` — the machine-local, untracked file, so a project's reviewed `.claude/settings.json` is never rewritten by an install: the caveman and ponytail plugins are enabled so their modes auto-activate on session start, and two reminder hooks are registered — `kvb-skills-tune` points you at `/kvb-skills-tune` at most once an hour, `kvb-skills-dream` points you at `/kvb-skills-dream` at most once every twelve hours per repository (stamped in that repository's git dir). A PreToolUse gate from the `kvb-org-organize` skill blocks edits to files in a git worktree until the session holds a claim (`skills/kvb-org-organize/scripts/council.py`), and its resource watch (`skills/kvb-org-organize/scripts/watch.py`) measures disk, Docker and caches, cleans up low-risk data and blocks new heavy jobs before the disk runs out; it stays inactive until `~/.config/agents-council/config.json` exists. Two hooks of the `kvb-org-perform` skill (`skills/kvb-org-perform/scripts/log.py`) log every Agent spawn with its task class, model and effort, and store the quality feedback a sandboxed session prints, in `~/.local/state/agents-council/subagent-model.jsonl`. Existing settings are preserved.
Both commands also patch the target's `.claude/settings.local.json` — the machine-local, untracked file, so a project's reviewed `.claude/settings.json` is never rewritten by an install: the caveman and ponytail plugins are enabled so their modes auto-activate on session start, and two reminder hooks are registered — `kvb-skill-tune` points you at `/kvb-skill-tune` at most once an hour, `kvb-skill-dream` points you at `/kvb-skill-dream` at most once every twelve hours per repository (stamped in that repository's git dir). A PreToolUse gate from the `kvb-org-organize` skill blocks edits to files in a git worktree until the session holds a claim (`skills/kvb-org-organize/scripts/council.py`), and its resource watch (`skills/kvb-org-organize/scripts/watch.py`) measures disk, Docker and caches, cleans up low-risk data and blocks new heavy jobs before the disk runs out; it stays inactive until `~/.config/agents-council/config.json` exists. Two hooks of the `kvb-org-perform` skill (`skills/kvb-org-perform/scripts/log.py`) log every Agent spawn with its task class, model and effort, and store the quality feedback a sandboxed session prints, in `~/.local/state/agents-council/subagent-model.jsonl`. Existing settings are preserved.
Restart your agent afterwards so it loads the new skills.
+5 -7
View File
@@ -1,8 +1,4 @@
#!/usr/bin/env node
// Enable the caveman and ponytail plugins in a Claude Code settings.json so
// their SessionStart hooks auto-activate both modes, register the kvb-skills-tune
// and kvb-skills-dream reminder hooks, the kvb-org-organize claim gate, the resource watch and the kvb-org-perform log. Merges non-destructively: existing marketplaces,
// plugins and hooks are preserved, hooks of renamed skills are repointed, unparseable files are left untouched.
"use strict";
const fs = require("fs");
@@ -19,10 +15,12 @@ const MARKETPLACES = {
ponytail: { source: { source: "github", repo: "DietrichGebert/ponytail" } },
};
const PLUGINS = { "caveman@caveman": true, "ponytail@ponytail": true };
const REMINDERS = ["kvb-skills-tune", "kvb-skills-dream"];
const REMINDERS = ["kvb-skill-tune", "kvb-skill-dream"];
const RENAMED = {
"autotune/hooks/reminder.sh": "kvb-skills-tune/hooks/reminder.sh",
"dream/hooks/reminder.sh": "kvb-skills-dream/hooks/reminder.sh",
"autotune/hooks/reminder.sh": "kvb-skill-tune/hooks/reminder.sh",
"dream/hooks/reminder.sh": "kvb-skill-dream/hooks/reminder.sh",
"kvb-skills-tune/hooks/reminder.sh": "kvb-skill-tune/hooks/reminder.sh",
"kvb-skills-dream/hooks/reminder.sh": "kvb-skill-dream/hooks/reminder.sh",
"democracy/scripts/council.py": "kvb-org-organize/scripts/council.py",
"democracy/scripts/watch.py": "kvb-org-organize/scripts/watch.py",
"subagent-model/scripts/log.py": "kvb-org-perform/scripts/log.py",
+1 -1
View File
@@ -36,5 +36,5 @@ A threshold the operator named ("95% confidence, break risk ≤5%") is a gate on
the work, handled as the `kvb-check-break-risk` skill's last section says, for both
numbers.
Objective: the `kvb-skills-tune` skill's **Objective** section. Two figures with their residuals replace a
Objective: the `kvb-skill-tune` skill's **Objective** section. Two figures with their residuals replace a
hedging paragraph and save the operator a verification round trip.
+1 -1
View File
@@ -68,7 +68,7 @@ Inside a sandbox that cannot write it, the script prints the line as
runs and the share with the same result: completed, not redone, no failing
test, no follow-up. A row is added, or a cheaper setting extended to a class,
only when the report shows at least 90 % over at least 20 rated runs, and only
after the operator confirms it (for example through `/kvb-skills-tune`). A row whose
after the operator confirms it (for example through `/kvb-skill-tune`). A row whose
rate falls below 90 % over 20 rated runs goes back to the default. `sonnet`
enters the table only this way, and a trial run on a setting the table does
not hold needs the operator's order.
+1 -1
View File
@@ -53,7 +53,7 @@ published). `done` is refused while one is open; a task ended short of them is
executing the task right now.
- Artifacts share this table and follow the `kvb-org-artefacts` skill:
record each one when it appears, drop it once deleted. Add every script or
tool the task ran (`--tool`): `tools` ranks them for `kvb-skills-dream`.
tool the task ran (`--tool`): `tools` ranks them for `kvb-skill-dream`.
- Priority is the rank of the `kvb-org-wsjf` skill: an operator's pin
(`--prio`) first, then WSJF from the estimates the chair collects; `show`
prints the rank as Prio and the score as WSJF.
@@ -1,12 +1,12 @@
---
name: kvb-skills-auto
name: kvb-skill-auto
description: >
Turn operator repetition into a skill. Trigger when the operator repeats an
instruction, correction or prompt pattern a third time in a conversation, or
says they keep repeating themselves, and the pattern is reusable.
---
Its objective is the `kvb-skills-tune` skill's **Objective** section: a skill is worth
Its objective is the `kvb-skill-tune` skill's **Objective** section: a skill is worth
creating only when it cuts tokens or time and raises quality; judge every
candidate against its three axes and rules.
@@ -19,7 +19,7 @@ vary), do the following:
a skill name suggestion, the trigger phrasing, and what the skill would
do. Ask whether to create it.
3. On approval, write the skill: persist it exactly as the
`kvb-skills-tune` skill's **Persistence** section specifies — locate the
`kvb-skill-tune` skill's **Persistence** section specifies — locate the
operator's skills repository, write `skills/<name>/SKILL.md` there (or in
the current project for project-specific behavior), then mirror the skill
directory into `~/.claude/skills/` and `~/.agents/skills/`, plus the
@@ -1,9 +1,9 @@
---
name: kvb-skills-dream
name: kvb-skill-dream
description: >
Consolidate the last 24 hours of work in this repository (what held, what
broke) into memory and improvement proposals.
Trigger on /kvb-skills-dream or a request to reflect on or learn from recent
Trigger on /kvb-skill-dream or a request to reflect on or learn from recent
work.
---
@@ -30,7 +30,7 @@ confidence the next run can now hold on each closed class, calibrated as the
Everything is read from the repository the skill runs in (`git rev-parse
--show-toplevel`); never mix in another project's history or memory. The default
window is the last 24 hours — the operator may widen it (`/kvb-skills-dream 7d`). State the
window is the last 24 hours — the operator may widen it (`/kvb-skill-dream 7d`). State the
repository and the window in one line before you start.
Run only when the operator asks. The 12-hour reminder hook
@@ -109,10 +109,10 @@ as radio buttons: the improvement, the evidence behind it, and the options
`Apply now`, `Record as a task`, `Skip`. Ranked by evidence, one question per
improvement. No repository file changes before an answer comes back.
## Step 7: kvb-skills-tune, kvb-skills-auto, kvb-transcend
## Step 7: kvb-skill-tune, kvb-skill-auto, kvb-transcend
With the dialectic done and memory written, run `kvb-skills-tune` — hand it this run's
findings as its evidence so it does not re-collect them — then `kvb-skills-auto`
With the dialectic done and memory written, run `kvb-skill-tune` — hand it this run's
findings as its evidence so it does not re-collect them — then `kvb-skill-auto`
for the operator corrections the window revealed, and `kvb-transcend` for the
scripts and tests the window's sessions wrote outside a repository. All keep
their own confirmation steps; the dream never writes a skill on their behalf.
@@ -1,9 +1,4 @@
#!/usr/bin/env bash
# UserPromptSubmit hook: emit the kvb-skills-dream hint at most once per interval and per
# repository. The stamp lives in the repository's git dir, so every repository
# keeps its own cadence; outside a git repository the hook stays silent.
# Stdout lands in the agent's context, which is why the payload tells the agent
# to show the hint and keep going instead of running kvb-skills-dream by itself.
set -euo pipefail
interval="${DREAM_INTERVAL_SECONDS:-43200}"
@@ -31,10 +26,10 @@ printf '%s\n' "${now}" >"${stamp}"
cat <<'HINT'
DREAM HINT: over twelve hours since the last hint for this repository. Show the
operator this notice verbatim as a short block, then continue with their
request. Do NOT run kvb-skills-dream, do not start replaying the history, do
request. Do NOT run kvb-skill-dream, do not start replaying the history, do
not ask questions about it.
💤 /kvb-skills-dream - consolidate the last 24h in this repository: what
💤 /kvb-skill-dream - consolidate the last 24h in this repository: what
held, what broke, which hypotheses follow, into memory and improvement
proposals.
Runs only when you trigger it.
@@ -1,8 +1,8 @@
---
name: kvb-skills-essence
name: kvb-skill-essence
description: >
Compact skills and memory to their essence after a health pass that finds
dead weight. Trigger on /kvb-skills-essence or when skills or MEMORY.md grow too long,
dead weight. Trigger on /kvb-skill-essence or when skills or MEMORY.md grow too long,
duplicate, or eat too much context.
---
@@ -1,8 +1,8 @@
---
name: kvb-skills-transcend
name: kvb-skill-transcend
description: >
Carry the session's skill use and own scripts into the skills repo.
Trigger on /kvb-skills-transcend.
Trigger on /kvb-skill-transcend.
---
What an agent improvised in one session dies with it: the helper script in its
@@ -31,7 +31,7 @@ One line each: `candidate -> source -> evidence`.
Per candidate one target: a script under an existing skill's `scripts/`, a
new skill, a sharper step in an existing skill, a memory entry instead, or
drop. Recurring moves without a script go to the `kvb-skills-tune` skill as
drop. Recurring moves without a script go to the `kvb-skill-tune` skill as
evidence. A script is portable only once its host paths, names and values
became explicit parameters (the `kvb-code-no-defaults` skill); one that needs
a secret, a credential or a single host is dropped or stays a memory entry.
@@ -46,7 +46,7 @@ diff.
## Step 4: write
- Write where the `kvb-skills-tune` skill's **Persistence** section says:
- Write where the `kvb-skill-tune` skill's **Persistence** section says:
the skills repository as source of truth, then the live mirrors.
- Read every script whole before carrying it; strip comments the way the
`kvb-code-no-comments` skill does.
@@ -1,9 +1,9 @@
---
name: kvb-skills-tune
name: kvb-skill-tune
description: >
Generate new skills or optimize existing ones (fewer tokens and errors,
persistent learnings), each proposal confirmed. Trigger ONLY on an explicit
request: /kvb-skills-tune, "autotune", "tune the skills".
request: /kvb-skill-tune, "autotune", "tune the skills".
---
Autotune turns observed friction into durable skills. It runs on demand only,
@@ -12,8 +12,8 @@ skill it writes or rewrites lands in the operator's skills repository.
## Objective
The shared objective of the self-improving skills (kvb-skills-tune,
kvb-skills-auto, kvb-check-confidence): every skill, rule, or report the agent produces about its own
The shared objective of the self-improving skills (kvb-skill-tune,
kvb-skill-auto, kvb-check-confidence): every skill, rule, or report the agent produces about its own
working is judged on three axes at once. A proposal that cannot name the axis
it moves is not an optimization, it is a preference.
@@ -42,7 +42,7 @@ it moves is not an optimization, it is a preference.
## Trigger discipline
- Run **only** when the operator asks: `/kvb-skills-tune`, "autotune", "tune the
- Run **only** when the operator asks: `/kvb-skill-tune`, "autotune", "tune the
skills", or an equivalent instruction.
- The 60-minute reminder hook is a hint for the operator, not a trigger. When it
fires, print the hint and continue the operator's actual request.
@@ -63,7 +63,7 @@ where readable, from the project's memory and agent instruction files:
- **Lost learnings**: facts re-derived this session that a memory file or a
skill should already have carried.
- **Own scripts**: helpers the agent wrote outside any repository belong to
the `kvb-skills-transcend` skill, which carries them over; this skill
the `kvb-skill-transcend` skill, which carries them over; this skill
takes the frictions they reveal.
List each finding as one line: `symptom -> cost -> candidate fix`. Skip
@@ -77,7 +77,7 @@ option, and never write a file before the answers are in.
Ask in this order, at most four questions per call:
1. **Scope** - "What should kvb-skills-tune do this run?" Options: `Create a new
1. **Scope** - "What should kvb-skill-tune do this run?" Options: `Create a new
skill`, `Optimize an existing skill`, `Both`, plus the ranked findings if
more than one candidate exists.
2. **Target** - which finding becomes a skill, or which existing skill gets
@@ -132,7 +132,7 @@ reverse — remove the mirrors too, or the old name keeps firing.
- Optimizing an existing skill means the file gets shorter or sharper, not
longer. Delete rules the agent already follows, merge duplicated ones, and
cut every sentence that does not change an action. Route that compaction
through the `kvb-skills-essence` skill instead of trimming by hand: it runs a health
through the `kvb-skill-essence` skill instead of trimming by hand: it runs a health
pass for dead pointers and rules duplicated across skills before cutting
anything, ranks by what loads in every session, and shows the diff before it
writes. It asks for its own scope — answer with the skill being optimized.
@@ -142,7 +142,7 @@ reverse — remove the mirrors too, or the old name keeps firing.
## Rules
- One skill per run unless the operator picked `Both`; a broad rewrite of the
whole collection is not what kvb-skills-tune is for.
whole collection is not what kvb-skill-tune is for.
- Never write a skill for one-off work, for secrets, or for behaviour an
existing skill already covers - propose extending that skill instead.
- The **Objective** section decides what counts as an improvement: at least
@@ -1,7 +1,4 @@
#!/usr/bin/env bash
# UserPromptSubmit hook: emit the kvb-skills-tune hint at most once per interval.
# Stdout lands in the agent's context, which is why the payload tells the agent
# to show the hint and keep going instead of running kvb-skills-tune by itself.
set -euo pipefail
interval="${AUTOTUNE_INTERVAL_SECONDS:-3600}"
@@ -24,9 +21,9 @@ printf '%s\n' "${now}" >"${stamp}"
cat <<'HINT'
AUTOTUNE HINT: over an hour since the last hint. Show the operator this notice
verbatim as a short block, then continue with their request. Do NOT run
kvb-skills-tune, do not propose skills, do not ask questions about it.
kvb-skill-tune, do not propose skills, do not ask questions about it.
💡 /kvb-skills-tune - generate a new skill or optimize an existing one
💡 /kvb-skill-tune - generate a new skill or optimize an existing one
(fewer tokens, fewer errors, more focus, learnings that persist).
Runs only when you trigger it.
HINT
+2 -2
View File
@@ -4,7 +4,7 @@ description: >
Run both transcend skills in one pass. Trigger on /kvb-transcend.
---
Collector for the `kvb-skills-transcend` and `kvb-code-transcend` skills, so
Collector for the `kvb-skill-transcend` and `kvb-code-transcend` skills, so
one harvest needs one round of questions. Its script finds the candidates and
remembers every decision, so a rescan offers only what is new or changed:
@@ -29,7 +29,7 @@ every time.
3. One `kvb-think-listen` round and one confirmation for all candidates,
grouped by target: the skills repository or the current repository. In
the skills repository itself, scripts and skills go to
`kvb-skills-transcend`, tests to `kvb-code-transcend`.
`kvb-skill-transcend`, tests to `kvb-code-transcend`.
4. Hand each confirmed candidate to its skill's write step, then `mark` every
offered candidate with its verdict.
5. One table: `| Candidate | Source | Target | Verdict |`.
+9 -9
View File
@@ -1,4 +1,4 @@
"""Validate the kvb-skills-tune reminder hook and its settings registration."""
"""Validate the kvb-skill-tune reminder hook and its settings registration."""
from __future__ import annotations
@@ -10,8 +10,8 @@ import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
SKILL = REPO_ROOT / "skills" / "kvb-skills-tune" / "SKILL.md"
HOOK = REPO_ROOT / "skills" / "kvb-skills-tune" / "hooks" / "reminder.sh"
SKILL = REPO_ROOT / "skills" / "kvb-skill-tune" / "SKILL.md"
HOOK = REPO_ROOT / "skills" / "kvb-skill-tune" / "hooks" / "reminder.sh"
PATCHER = REPO_ROOT / "scripts" / "enable-plugins.js"
@@ -33,7 +33,7 @@ def _run_hook(stamp: Path, interval: str = "3600") -> subprocess.CompletedProces
class TestAutotuneSkill(unittest.TestCase):
def test_skill_is_trigger_only(self):
text = SKILL.read_text(encoding="utf-8")
self.assertIn("name: kvb-skills-tune", text)
self.assertIn("name: kvb-skill-tune", text)
self.assertIn("multiSelect: false", text)
@@ -51,7 +51,7 @@ class TestSkillPersistence(unittest.TestCase):
self.assertIn(target, text)
def test_autoskill_routes_to_the_same_persistence(self):
text = (REPO_ROOT / "skills" / "kvb-skills-auto" / "SKILL.md").read_text(
text = (REPO_ROOT / "skills" / "kvb-skill-auto" / "SKILL.md").read_text(
encoding="utf-8"
)
for target in ("Persistence", "~/.claude/skills/", "~/.agents/skills/"):
@@ -66,7 +66,7 @@ class TestAutotuneHook(unittest.TestCase):
def test_first_run_hints_and_stamps(self):
result = _run_hook(self.stamp)
self.assertIn("/kvb-skills-tune", result.stdout)
self.assertIn("/kvb-skill-tune", result.stdout)
self.assertTrue(self.stamp.is_file())
def test_second_run_is_silent_within_interval(self):
@@ -75,12 +75,12 @@ class TestAutotuneHook(unittest.TestCase):
def test_hint_returns_after_the_interval(self):
_run_hook(self.stamp)
self.assertIn("/kvb-skills-tune", _run_hook(self.stamp, interval="0").stdout)
self.assertIn("/kvb-skill-tune", _run_hook(self.stamp, interval="0").stdout)
def test_corrupt_stamp_does_not_crash(self):
self.stamp.parent.mkdir(parents=True, exist_ok=True)
self.stamp.write_text("not-a-timestamp\n", encoding="utf-8")
self.assertIn("/kvb-skills-tune", _run_hook(self.stamp).stdout)
self.assertIn("/kvb-skill-tune", _run_hook(self.stamp).stdout)
@unittest.skipUnless(shutil.which("node"), "node not installed")
@@ -100,7 +100,7 @@ class TestSettingsRegistration(unittest.TestCase):
self._patch()
data = self._patch()
entries = json.dumps(data["hooks"]["UserPromptSubmit"])
self.assertEqual(entries.count("kvb-skills-tune/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-skill-tune/hooks/reminder.sh"), 1)
def test_existing_hooks_preserved(self):
self.settings.write_text(
+14 -14
View File
@@ -10,8 +10,8 @@ import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
SKILL = REPO_ROOT / "skills" / "kvb-skills-dream" / "SKILL.md"
HOOK = REPO_ROOT / "skills" / "kvb-skills-dream" / "hooks" / "reminder.sh"
SKILL = REPO_ROOT / "skills" / "kvb-skill-dream" / "SKILL.md"
HOOK = REPO_ROOT / "skills" / "kvb-skill-dream" / "hooks" / "reminder.sh"
PATCHER = REPO_ROOT / "scripts" / "enable-plugins.js"
@@ -38,7 +38,7 @@ def _run_hook(
class TestDreamSkill(unittest.TestCase):
def test_skill_is_trigger_only(self):
text = SKILL.read_text(encoding="utf-8")
self.assertIn("name: kvb-skills-dream", text)
self.assertIn("name: kvb-skill-dream", text)
self.assertIn("multiSelect: false", text)
@@ -51,7 +51,7 @@ class TestDreamHook(unittest.TestCase):
def test_first_run_hints_and_stamps(self):
result = _run_hook(self.cwd, self.stamp)
self.assertIn("/kvb-skills-dream", result.stdout)
self.assertIn("/kvb-skill-dream", result.stdout)
self.assertTrue(self.stamp.is_file())
def test_second_run_is_silent_within_interval(self):
@@ -60,12 +60,12 @@ class TestDreamHook(unittest.TestCase):
def test_hint_returns_after_the_interval(self):
_run_hook(self.cwd, self.stamp)
self.assertIn("/kvb-skills-dream", _run_hook(self.cwd, self.stamp, interval="0").stdout)
self.assertIn("/kvb-skill-dream", _run_hook(self.cwd, self.stamp, interval="0").stdout)
def test_corrupt_stamp_does_not_crash(self):
self.stamp.parent.mkdir(parents=True, exist_ok=True)
self.stamp.write_text("not-a-timestamp\n", encoding="utf-8")
self.assertIn("/kvb-skills-dream", _run_hook(self.cwd, self.stamp).stdout)
self.assertIn("/kvb-skill-dream", _run_hook(self.cwd, self.stamp).stdout)
def test_outside_a_repository_it_stays_silent(self):
self.assertEqual(_run_hook(self.cwd).stdout, "")
@@ -78,7 +78,7 @@ class TestDreamHook(unittest.TestCase):
subprocess.run(
["git", "init", "-q", str(repo)], check=True, capture_output=True
)
self.assertIn("/kvb-skills-dream", _run_hook(repo).stdout)
self.assertIn("/kvb-skill-dream", _run_hook(repo).stdout)
self.assertTrue((repo / ".git" / ".dream-stamp").is_file())
self.assertEqual(_run_hook(repo).stdout, "")
@@ -103,7 +103,7 @@ class TestDreamHook(unittest.TestCase):
subprocess.run(
["git", *args], check=True, capture_output=True, env=env, cwd=str(repo)
)
self.assertIn("/kvb-skills-dream", _run_hook(repo).stdout)
self.assertIn("/kvb-skill-dream", _run_hook(repo).stdout)
self.assertEqual(_run_hook(tree).stdout, "")
@@ -124,8 +124,8 @@ class TestSettingsRegistration(unittest.TestCase):
self._patch()
data = self._patch()
entries = json.dumps(data["hooks"]["UserPromptSubmit"])
self.assertEqual(entries.count("kvb-skills-dream/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-skills-tune/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-skill-dream/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-skill-tune/hooks/reminder.sh"), 1)
gate = json.dumps(data["hooks"]["PreToolUse"])
self.assertEqual(gate.count("kvb-org-organize/scripts/council.py"), 1)
self.assertEqual(gate.count("kvb-org-organize/scripts/watch.py"), 1)
@@ -147,7 +147,7 @@ class TestSettingsRegistration(unittest.TestCase):
"hooks": {
"UserPromptSubmit": [
hook(f'bash "{base}/autotune/hooks/reminder.sh"'),
hook(f'bash "{base}/dream/hooks/reminder.sh"'),
hook(f'bash "{base}/kvb-skills-dream/hooks/reminder.sh"'),
hook(f'python3 "{base}/democracy/scripts/watch.py" watch'),
hook("echo keep"),
],
@@ -167,12 +167,12 @@ class TestSettingsRegistration(unittest.TestCase):
)
data = self._patch()
text = json.dumps(data["hooks"])
for old in ("/autotune/", "/dream/", "/democracy/", "/subagent-model/"):
for old in ("/autotune/", "/kvb-skills-dream/", "/democracy/", "/subagent-model/"):
self.assertNotIn(f"skills{old}", text)
self.assertIn("echo keep", text)
entries = json.dumps(data["hooks"]["UserPromptSubmit"])
self.assertEqual(entries.count("kvb-skills-dream/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-skills-tune/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-skill-dream/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-skill-tune/hooks/reminder.sh"), 1)
self.assertEqual(entries.count("kvb-org-organize/scripts/watch.py"), 1)
gate = json.dumps(data["hooks"]["PreToolUse"])
self.assertEqual(gate.count("kvb-org-organize/scripts/council.py"), 1)
+1 -1
View File
@@ -9,7 +9,7 @@ from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
SKILLS = REPO_ROOT / "skills"
README = REPO_ROOT / "README.md"
CATEGORIES = ("think", "create", "code", "ci", "git", "check", "org", "skills")
CATEGORIES = ("think", "create", "code", "ci", "git", "check", "org", "skill")
UNCATEGORIZED = frozenset({"kvb-cleanup", "kvb-help", "kvb-transcend"})
SPEC = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$")
SCHEMA = re.compile(rf"^kvb-(?:{'|'.join(CATEGORIES)})-[a-z0-9]+(?:-[a-z0-9]+)*$")
+5 -5
View File
@@ -1,4 +1,4 @@
"""The self-improving skills must share the objective held by kvb-skills-tune."""
"""The self-improving skills must share the objective held by kvb-skill-tune."""
from __future__ import annotations
@@ -7,12 +7,12 @@ from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
SKILLS = REPO_ROOT / "skills"
ROUTERS = ("kvb-skills-auto", "kvb-check-confidence")
ROUTERS = ("kvb-skill-auto", "kvb-check-confidence")
class TestOptimizeObjective(unittest.TestCase):
def test_autotune_names_the_three_axes(self):
text = (SKILLS / "kvb-skills-tune" / "SKILL.md").read_text(encoding="utf-8")
text = (SKILLS / "kvb-skill-tune" / "SKILL.md").read_text(encoding="utf-8")
self.assertIn("## Objective", text)
for axis in ("**Tokens**", "**Time**", "**Quality**"):
self.assertIn(axis, text)
@@ -21,9 +21,9 @@ class TestOptimizeObjective(unittest.TestCase):
for skill in ROUTERS:
text = (SKILLS / skill / "SKILL.md").read_text(encoding="utf-8")
self.assertIn(
"`kvb-skills-tune` skill's **Objective**",
"`kvb-skill-tune` skill's **Objective**",
text,
f"{skill} does not route to the kvb-skills-tune objective",
f"{skill} does not route to the kvb-skill-tune objective",
)