refactor(skills)!: rename every skill to kvb-<category>-<name>
All 38 first-party skills move to kvb-<category>-<name> with the categories think, create, code, ci, git, check, org and skills; kvb-cleanup and kvb-help are the only uncategorized names. tests/test_naming.py pins the schema, the Agent Skills name rules and the README organigram. enable-plugins.js repoints hook commands of renamed skills in settings.local.json so a stale script path cannot block tools. BREAKING CHANGE: the old skill names and slash commands (/commit-next, /dream, /autotune, ...) are gone; use their kvb- successors listed in the README organigram. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
64403cefca
commit
624baccfc4
59 files changed
+504
-312
No files matched your search
@@ -1,4 +1,4 @@
|
||||
"""Validate the autotune reminder hook and its settings registration."""
|
||||
"""Validate the kvb-skills-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" / "autotune" / "SKILL.md"
|
||||
HOOK = REPO_ROOT / "skills" / "autotune" / "hooks" / "reminder.sh"
|
||||
SKILL = REPO_ROOT / "skills" / "kvb-skills-tune" / "SKILL.md"
|
||||
HOOK = REPO_ROOT / "skills" / "kvb-skills-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: autotune", text)
|
||||
self.assertIn("name: kvb-skills-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" / "autoskill" / "SKILL.md").read_text(
|
||||
text = (REPO_ROOT / "skills" / "kvb-skills-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("/autotune", result.stdout)
|
||||
self.assertIn("/kvb-skills-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("/autotune", _run_hook(self.stamp, interval="0").stdout)
|
||||
self.assertIn("/kvb-skills-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("/autotune", _run_hook(self.stamp).stdout)
|
||||
self.assertIn("/kvb-skills-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("autotune/hooks/reminder.sh"), 1)
|
||||
self.assertEqual(entries.count("kvb-skills-tune/hooks/reminder.sh"), 1)
|
||||
|
||||
def test_existing_hooks_preserved(self):
|
||||
self.settings.write_text(
|
||||
|
||||
@@ -11,8 +11,8 @@ import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
CI_FAILURES = REPO_ROOT / "skills" / "ci-artifacts" / "scripts" / "ci_failures.py"
|
||||
FIND_FIX = REPO_ROOT / "skills" / "merge-fix" / "scripts" / "find_fix.py"
|
||||
CI_FAILURES = REPO_ROOT / "skills" / "kvb-ci-artifacts" / "scripts" / "ci_failures.py"
|
||||
FIND_FIX = REPO_ROOT / "skills" / "kvb-git-merge-fix" / "scripts" / "find_fix.py"
|
||||
|
||||
|
||||
def _load(path: Path):
|
||||
@@ -55,7 +55,7 @@ class TestCiFailures(unittest.TestCase):
|
||||
"Ran 41 tests in 1.0s\n"
|
||||
"OK\n"
|
||||
"UP017 [*] Use `datetime.UTC` alias\n"
|
||||
" --> skills/democracy/scripts/council.py:92:40\n"
|
||||
" --> skills/kvb-org-organize/scripts/council.py:92:40\n"
|
||||
"Found 1 error.\n"
|
||||
"make: *** [Makefile:15: test] Error 1\n"
|
||||
"##[error]Process completed with exit code 2.\n"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Exercise the democracy council script: election, staleness, release, gate."""
|
||||
"""Exercise the kvb-org-organize council script: election, staleness, release, gate."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -10,7 +10,7 @@ import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
COUNCIL = REPO_ROOT / "skills" / "democracy" / "scripts" / "council.py"
|
||||
COUNCIL = REPO_ROOT / "skills" / "kvb-org-organize" / "scripts" / "council.py"
|
||||
|
||||
|
||||
class TestCouncil(unittest.TestCase):
|
||||
|
||||
+59
-16
@@ -10,8 +10,8 @@ import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
SKILL = REPO_ROOT / "skills" / "dream" / "SKILL.md"
|
||||
HOOK = REPO_ROOT / "skills" / "dream" / "hooks" / "reminder.sh"
|
||||
SKILL = REPO_ROOT / "skills" / "kvb-skills-dream" / "SKILL.md"
|
||||
HOOK = REPO_ROOT / "skills" / "kvb-skills-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: dream", text)
|
||||
self.assertIn("name: kvb-skills-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("/dream", result.stdout)
|
||||
self.assertIn("/kvb-skills-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("/dream", _run_hook(self.cwd, self.stamp, interval="0").stdout)
|
||||
self.assertIn("/kvb-skills-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("/dream", _run_hook(self.cwd, self.stamp).stdout)
|
||||
self.assertIn("/kvb-skills-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("/dream", _run_hook(repo).stdout)
|
||||
self.assertIn("/kvb-skills-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("/dream", _run_hook(repo).stdout)
|
||||
self.assertIn("/kvb-skills-dream", _run_hook(repo).stdout)
|
||||
self.assertEqual(_run_hook(tree).stdout, "")
|
||||
|
||||
|
||||
@@ -124,16 +124,59 @@ class TestSettingsRegistration(unittest.TestCase):
|
||||
self._patch()
|
||||
data = self._patch()
|
||||
entries = json.dumps(data["hooks"]["UserPromptSubmit"])
|
||||
self.assertEqual(entries.count("dream/hooks/reminder.sh"), 1)
|
||||
self.assertEqual(entries.count("autotune/hooks/reminder.sh"), 1)
|
||||
self.assertEqual(entries.count("kvb-skills-dream/hooks/reminder.sh"), 1)
|
||||
self.assertEqual(entries.count("kvb-skills-tune/hooks/reminder.sh"), 1)
|
||||
gate = json.dumps(data["hooks"]["PreToolUse"])
|
||||
self.assertEqual(gate.count("democracy/scripts/council.py"), 1)
|
||||
self.assertEqual(gate.count("democracy/scripts/watch.py"), 1)
|
||||
self.assertEqual(entries.count("democracy/scripts/watch.py"), 1)
|
||||
self.assertEqual(gate.count("kvb-org-organize/scripts/council.py"), 1)
|
||||
self.assertEqual(gate.count("kvb-org-organize/scripts/watch.py"), 1)
|
||||
self.assertEqual(entries.count("kvb-org-organize/scripts/watch.py"), 1)
|
||||
post = json.dumps(data["hooks"]["PostToolUse"])
|
||||
self.assertEqual(post.count("democracy/scripts/watch.py"), 1)
|
||||
self.assertEqual(gate.count("subagent-model/scripts/log.py"), 1)
|
||||
self.assertEqual(post.count("subagent-model/scripts/log.py"), 1)
|
||||
self.assertEqual(post.count("kvb-org-organize/scripts/watch.py"), 1)
|
||||
self.assertEqual(gate.count("kvb-org-perform/scripts/log.py"), 1)
|
||||
self.assertEqual(post.count("kvb-org-perform/scripts/log.py"), 1)
|
||||
|
||||
def test_hooks_of_renamed_skills_are_repointed(self):
|
||||
base = Path(self.tmp.name) / "skills"
|
||||
|
||||
def hook(command):
|
||||
return {"hooks": [{"type": "command", "command": command}]}
|
||||
|
||||
self.settings.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"hooks": {
|
||||
"UserPromptSubmit": [
|
||||
hook(f'bash "{base}/autotune/hooks/reminder.sh"'),
|
||||
hook(f'bash "{base}/dream/hooks/reminder.sh"'),
|
||||
hook(f'python3 "{base}/democracy/scripts/watch.py" watch'),
|
||||
hook("echo keep"),
|
||||
],
|
||||
"PreToolUse": [
|
||||
hook(f'python3 "{base}/democracy/scripts/council.py" gate'),
|
||||
hook(f'python3 "{base}/democracy/scripts/watch.py" block'),
|
||||
hook(f'python3 "{base}/subagent-model/scripts/log.py" spawn'),
|
||||
],
|
||||
"PostToolUse": [
|
||||
hook(f'python3 "{base}/democracy/scripts/watch.py" watch'),
|
||||
hook(f'python3 "{base}/subagent-model/scripts/log.py" persist'),
|
||||
],
|
||||
}
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
data = self._patch()
|
||||
text = json.dumps(data["hooks"])
|
||||
for old in ("/autotune/", "/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-org-organize/scripts/watch.py"), 1)
|
||||
gate = json.dumps(data["hooks"]["PreToolUse"])
|
||||
self.assertEqual(gate.count("kvb-org-organize/scripts/council.py"), 1)
|
||||
self.assertEqual(gate.count("kvb-org-perform/scripts/log.py"), 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
"""Pin every first-party skill name to kvb-<category>-<name> and the Agent Skills name rules."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import unittest
|
||||
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")
|
||||
UNCATEGORIZED = frozenset({"kvb-cleanup", "kvb-help"})
|
||||
SPEC = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$")
|
||||
SCHEMA = re.compile(rf"^kvb-(?:{'|'.join(CATEGORIES)})-[a-z0-9]+(?:-[a-z0-9]+)*$")
|
||||
NAME_LINE = re.compile(r"^name:\s*(\S+)\s*$", re.MULTILINE)
|
||||
ORGANIGRAM_EDGE = re.compile(r"^\s*(\w+) --> \w+\[(kvb-[a-z0-9-]+)\]\s*$", re.MULTILINE)
|
||||
|
||||
|
||||
class TestSkillNames(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.names = sorted(p.name for p in SKILLS.iterdir() if p.is_dir())
|
||||
|
||||
def test_name_field_matches_the_directory(self):
|
||||
for name in self.names:
|
||||
with self.subTest(skill=name):
|
||||
text = (SKILLS / name / "SKILL.md").read_text(encoding="utf-8")
|
||||
match = NAME_LINE.search(text)
|
||||
self.assertIsNotNone(match, "SKILL.md has no name: line")
|
||||
self.assertEqual(match.group(1), name)
|
||||
|
||||
def test_names_follow_the_agent_skills_rules(self):
|
||||
for name in self.names:
|
||||
with self.subTest(skill=name):
|
||||
self.assertRegex(name, SPEC)
|
||||
self.assertLessEqual(len(name), 64)
|
||||
self.assertNotRegex(name, "anthropic|claude")
|
||||
|
||||
def test_names_use_a_listed_category(self):
|
||||
for name in self.names:
|
||||
if name in UNCATEGORIZED:
|
||||
continue
|
||||
with self.subTest(skill=name):
|
||||
self.assertRegex(name, SCHEMA)
|
||||
|
||||
def test_every_category_and_exception_is_in_use(self):
|
||||
for category in CATEGORIES:
|
||||
with self.subTest(category=category):
|
||||
self.assertTrue(
|
||||
any(n.startswith(f"kvb-{category}-") for n in self.names),
|
||||
f"category {category} has no skill",
|
||||
)
|
||||
self.assertLessEqual(UNCATEGORIZED, set(self.names))
|
||||
|
||||
def test_readme_organigram_places_every_skill_under_its_category(self):
|
||||
text = README.read_text(encoding="utf-8")
|
||||
section = text.split("## Organigram", 1)[1].split("\n## ", 1)[0]
|
||||
edges = ORGANIGRAM_EDGE.findall(section)
|
||||
self.assertEqual(sorted(name for _, name in edges), self.names)
|
||||
for parent, name in edges:
|
||||
with self.subTest(skill=name):
|
||||
expected = "none" if name in UNCATEGORIZED else name.split("-")[1]
|
||||
self.assertEqual(parent, expected)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,4 +1,4 @@
|
||||
"""The self-improving skills must share the objective held by autotune."""
|
||||
"""The self-improving skills must share the objective held by kvb-skills-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 = ("autoskill", "confidence")
|
||||
ROUTERS = ("kvb-skills-auto", "kvb-check-confidence")
|
||||
|
||||
|
||||
class TestOptimizeObjective(unittest.TestCase):
|
||||
def test_autotune_names_the_three_axes(self):
|
||||
text = (SKILLS / "autotune" / "SKILL.md").read_text(encoding="utf-8")
|
||||
text = (SKILLS / "kvb-skills-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(
|
||||
"`autotune` skill's **Objective**",
|
||||
"`kvb-skills-tune` skill's **Objective**",
|
||||
text,
|
||||
f"{skill} does not route to the autotune objective",
|
||||
f"{skill} does not route to the kvb-skills-tune objective",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Validate the shortcuts skill table: rows present, sorted, ``soc``-prefixed,
|
||||
"""Validate the kvb-skills-shortcuts skill table: rows present, sorted, ``soc``-prefixed,
|
||||
no two adjacent consonants."""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -8,7 +8,7 @@ import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
SKILL = REPO_ROOT / "skills" / "shortcuts" / "SKILL.md"
|
||||
SKILL = REPO_ROOT / "skills" / "kvb-skills-shortcuts" / "SKILL.md"
|
||||
|
||||
ROW_RE = re.compile(r"^\|\s*`(?P<shortcut>[^`]+)`\s*\|")
|
||||
VOWELS = frozenset("aeiou")
|
||||
@@ -37,7 +37,7 @@ class TestShortcuts(unittest.TestCase):
|
||||
]
|
||||
|
||||
def test_shortcuts_present(self):
|
||||
self.assertTrue(self.shortcuts, "no shortcut rows found in shortcuts skill")
|
||||
self.assertTrue(self.shortcuts, "no shortcut rows found in kvb-skills-shortcuts skill")
|
||||
|
||||
def test_shortcuts_sorted_ascending(self):
|
||||
self.assertEqual(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Exercise the subagent-model log: spawn hook, feedback, sandbox persist, report."""
|
||||
"""Exercise the kvb-org-perform log: spawn hook, feedback, sandbox persist, report."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -10,7 +10,7 @@ import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
LOG_PY = REPO_ROOT / "skills" / "subagent-model" / "scripts" / "log.py"
|
||||
LOG_PY = REPO_ROOT / "skills" / "kvb-org-perform" / "scripts" / "log.py"
|
||||
|
||||
|
||||
class TestSubagentModelLog(unittest.TestCase):
|
||||
@@ -66,13 +66,13 @@ class TestSubagentModelLog(unittest.TestCase):
|
||||
|
||||
def test_sandboxed_feedback_is_printed_and_stored_by_the_persist_hook(self):
|
||||
stdout = self._pending_feedback()
|
||||
self._persist("python3 /x/skills/subagent-model/scripts/log.py feedback --description 'd'", stdout)
|
||||
self._persist("python3 /x/skills/kvb-org-perform/scripts/log.py feedback --description 'd'", stdout)
|
||||
self.assertEqual(self._lines()[0]["kind"], "feedback")
|
||||
|
||||
def test_persist_rejects_pending_lines_it_did_not_get_from_a_bare_log_call(self):
|
||||
stdout = self._pending_feedback()
|
||||
line = json.loads(stdout[len("pending: "):])
|
||||
call = "/x/skills/subagent-model/scripts/log.py feedback --description d"
|
||||
call = "/x/skills/kvb-org-perform/scripts/log.py feedback --description d"
|
||||
for command, output, session in (
|
||||
("cat ci.log", stdout, "s1"),
|
||||
(f"{call}; cat ci.log", stdout, "s1"),
|
||||
@@ -82,7 +82,7 @@ class TestSubagentModelLog(unittest.TestCase):
|
||||
(call, stdout + stdout, "s1"),
|
||||
(call, "pending: " + json.dumps({**line, "extra": 1}), "s1"),
|
||||
(call, "pending: " + json.dumps({**line, "completed": "yes"}), "s1"),
|
||||
("/x/skills/subagent-model/scripts/log.py followup --description d", stdout, "s1"),
|
||||
("/x/skills/kvb-org-perform/scripts/log.py followup --description d", stdout, "s1"),
|
||||
):
|
||||
self._persist(command, output, session)
|
||||
self.assertFalse(self.log.exists())
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
"""Exercise the democracy resource watch: stages, projection, block, admit, alerts, caches."""
|
||||
"""Exercise the kvb-org-organize resource watch: stages, projection, block, admit, alerts, caches."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -13,7 +13,7 @@ import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
WATCH = REPO_ROOT / "skills" / "democracy" / "scripts" / "watch.py"
|
||||
WATCH = REPO_ROOT / "skills" / "kvb-org-organize" / "scripts" / "watch.py"
|
||||
LIMITS = {"warn": 40, "stop": 15, "critical": 12}
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user