feat(pouch): add skill structure audit to cut token waste
Teach pouch to optimize a named skill's loading layout: keep SKILL.md as a router, move mode-specific steps to references, and measure footprint with an audit script instead of dumping the whole skill into context.
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
SCRIPT = (
|
||||
REPO_ROOT
|
||||
/ "skills"
|
||||
/ "pouch"
|
||||
/ "scripts"
|
||||
/ "audit_skill_structure.py"
|
||||
)
|
||||
|
||||
|
||||
class AuditSkillStructureTests(unittest.TestCase):
|
||||
def _write_skill(self, root: Path, *, body: str, description: str = "short") -> Path:
|
||||
skill = root / "demo"
|
||||
skill.mkdir()
|
||||
(skill / "SKILL.md").write_text(
|
||||
"---\n"
|
||||
"name: demo\n"
|
||||
f"description: {description}\n"
|
||||
"---\n\n"
|
||||
f"{body}\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
return skill
|
||||
|
||||
def _run(self, skill_dir: Path) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(
|
||||
[sys.executable, str(SCRIPT), str(skill_dir)],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
def test_reports_within_budget_and_reference_size(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
skill = self._write_skill(
|
||||
Path(tmp),
|
||||
body="# Demo\n\n## 步骤\n\n1. 做一件事。\n",
|
||||
)
|
||||
(skill / "references").mkdir()
|
||||
(skill / "references" / "guide.md").write_text(
|
||||
"# Guide\n\n细节。\n", encoding="utf-8"
|
||||
)
|
||||
result = self._run(skill)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertIn("status: within-budget", result.stdout)
|
||||
self.assertIn("## 步骤", result.stdout)
|
||||
self.assertIn("references/guide.md", result.stdout)
|
||||
self.assertIn("notes:", result.stdout)
|
||||
self.assertIn("reference 未被 SKILL.md 链接: guide.md", result.stdout)
|
||||
|
||||
def test_flags_hard_budget_and_vague_references(self) -> None:
|
||||
padding = "这是一段占位说明,用来把正文撑过硬顶。" * 400
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
skill = self._write_skill(
|
||||
Path(tmp),
|
||||
body="# Demo\n\n详见 references/。\n\n" + padding,
|
||||
)
|
||||
result = self._run(skill)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertIn("status: over-hard-budget", result.stdout)
|
||||
self.assertIn("HARD SKILL.md", result.stdout)
|
||||
self.assertIn("见 references/", result.stdout)
|
||||
|
||||
def test_missing_skill_exits_2(self) -> None:
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
result = self._run(Path(tmp) / "missing")
|
||||
self.assertEqual(result.returncode, 2)
|
||||
self.assertIn("找不到 skill", result.stderr)
|
||||
Reference in New Issue
Block a user