chill-agent 1.4.0 → 1.6.0
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/.agents/rules/ponytail.md +30 -0
- package/.claude-plugin/contrast-mcp-launcher.mjs +31 -0
- package/.claude-plugin/marketplace.json +17 -0
- package/.claude-plugin/plugin.json +8 -3
- package/.cline-plugin/plugin.js +6 -0
- package/.clinerules/ponytail.md +30 -0
- package/.codex-plugin/plugin.json +4 -3
- package/.cursor/rules/ponytail.mdc +36 -0
- package/.cursor-plugin/marketplace.json +16 -0
- package/.cursor-plugin/plugin.json +2 -2
- package/.devin-plugin/plugin.json +2 -2
- package/.grok-plugin/marketplace.json +16 -0
- package/.kimi-plugin/plugin.json +11 -29
- package/.kiro/steering/ponytail.md +35 -0
- package/.omp-plugin/marketplace.json +15 -0
- package/.openclaw/skills/ponytail/SKILL.md +47 -0
- package/.openclaw/skills/ponytail-audit/SKILL.md +100 -0
- package/.openclaw/skills/ponytail-debt/SKILL.md +43 -0
- package/.openclaw/skills/ponytail-gain/SKILL.md +51 -0
- package/.openclaw/skills/ponytail-help/SKILL.md +76 -0
- package/.openclaw/skills/ponytail-review/SKILL.md +98 -0
- package/.opencode/command/ponytail-audit.md +5 -0
- package/.opencode/command/ponytail-debt.md +5 -0
- package/.opencode/command/ponytail-gain.md +5 -0
- package/.opencode/command/ponytail-help.md +5 -0
- package/.opencode/command/ponytail-review.md +5 -0
- package/.opencode/command/ponytail.md +5 -0
- package/.opencode/plugins/index.js +4 -0
- package/.opencode/plugins/ponytail-frontmatter.cjs +63 -0
- package/.opencode/plugins/ponytail.mjs +206 -0
- package/.qoder-plugin/plugin.json +12 -26
- package/.windsurf/rules/ponytail.md +30 -0
- package/AGENTS.md +32 -0
- package/__init__.py +228 -0
- package/assets/antislop-banner.png +0 -0
- package/assets/band/band.png +0 -0
- package/assets/band/board.png +0 -0
- package/assets/benchmark-3model.svg +21 -0
- package/assets/benchmark-agentic.svg +62 -0
- package/assets/codex-icon.png +0 -0
- package/assets/compare/code/after.webp +0 -0
- package/assets/compare/code/before.webp +0 -0
- package/assets/compare/text/after.webp +0 -0
- package/assets/compare/text/before.webp +0 -0
- package/assets/compare/ui/w-all.webp +0 -0
- package/assets/compare/ui/w-antislop.webp +0 -0
- package/assets/compare/ui/w-design.webp +0 -0
- package/assets/compare/ui/without.webp +0 -0
- package/assets/logo-dark.png +0 -0
- package/assets/logo-dark.svg +115 -0
- package/assets/logo-greenpt-dark.svg +27 -0
- package/assets/logo-greenpt.svg +27 -0
- package/assets/logo.png +0 -0
- package/assets/photoreunion-logo-dark.svg +4 -0
- package/assets/photoreunion-logo-light.svg +4 -0
- package/assets/profile.png +0 -0
- package/assets/retriever-logo-dark.svg +4 -0
- package/assets/retriever-logo-light.svg +4 -0
- package/assets/stories-banner.png +0 -0
- package/assets/v5/audit-es.png +0 -0
- package/assets/v5/audit-ja.png +0 -0
- package/assets/v5/audit-ko.png +0 -0
- package/assets/v5/audit-zh-CN.png +0 -0
- package/assets/v5/audit.png +0 -0
- package/assets/v5/beforeafter-es.png +0 -0
- package/assets/v5/beforeafter-ja.png +0 -0
- package/assets/v5/beforeafter-ko.png +0 -0
- package/assets/v5/beforeafter-zh-CN.png +0 -0
- package/assets/v5/beforeafter.png +0 -0
- package/assets/v5/chart-es.png +0 -0
- package/assets/v5/chart-ja.png +0 -0
- package/assets/v5/chart-ko.png +0 -0
- package/assets/v5/chart-zh-CN.png +0 -0
- package/assets/v5/chart.png +0 -0
- package/assets/v5/hero-es.jpg +0 -0
- package/assets/v5/hero-ja.jpg +0 -0
- package/assets/v5/hero-ko.jpg +0 -0
- package/assets/v5/hero-zh-CN.jpg +0 -0
- package/assets/v5/hero.jpg +0 -0
- package/assets/v5/ladder-es.png +0 -0
- package/assets/v5/ladder-ja.png +0 -0
- package/assets/v5/ladder-ko.png +0 -0
- package/assets/v5/ladder-zh-CN.png +0 -0
- package/assets/v5/ladder.png +0 -0
- package/assets/v5/review-es.png +0 -0
- package/assets/v5/review-ja.png +0 -0
- package/assets/v5/review-ko.png +0 -0
- package/assets/v5/review-zh-CN.png +0 -0
- package/assets/v5/review.png +0 -0
- package/assets/v5/tests-es.png +0 -0
- package/assets/v5/tests-ja.png +0 -0
- package/assets/v5/tests-ko.png +0 -0
- package/assets/v5/tests-zh-CN.png +0 -0
- package/assets/v5/tests.png +0 -0
- package/assets/waitlist-banner-es.png +0 -0
- package/assets/waitlist-banner-ja.png +0 -0
- package/assets/waitlist-banner-ko.png +0 -0
- package/assets/waitlist-banner-zh-CN.png +0 -0
- package/assets/waitlist-banner.png +0 -0
- package/benchmarks/.env.example +2 -0
- package/benchmarks/agentic/README.md +201 -0
- package/benchmarks/agentic/answer.py +288 -0
- package/benchmarks/agentic/compare.py +93 -0
- package/benchmarks/agentic/complete.py +178 -0
- package/benchmarks/agentic/judge.py +198 -0
- package/benchmarks/agentic/run.py +543 -0
- package/benchmarks/agentic/tasks.py +1544 -0
- package/benchmarks/arms/baseline.js +2 -0
- package/benchmarks/arms/caveman-SKILL.md +67 -0
- package/benchmarks/arms/caveman.js +8 -0
- package/benchmarks/arms/ponytail.js +8 -0
- package/benchmarks/behavior.js +54 -0
- package/benchmarks/behavior.yaml +40 -0
- package/benchmarks/correctness.js +291 -0
- package/benchmarks/loc.js +17 -19
- package/benchmarks/promptfooconfig.gemini.yaml +32 -0
- package/benchmarks/promptfooconfig.gpt-newest.yaml +33 -0
- package/benchmarks/promptfooconfig.yaml +41 -0
- package/benchmarks/results/2026-06-12-caveman-vs-ponytail.md +71 -0
- package/benchmarks/results/2026-06-12-v4-hardening-vs-caveman.md +125 -0
- package/benchmarks/results/2026-06-15-llama3.2-local.md +82 -0
- package/benchmarks/results/2026-06-16-correctness-gate-fix.md +113 -0
- package/benchmarks/results/2026-06-16-robustness-audit.md +135 -0
- package/benchmarks/results/2026-06-17-agentic-safety.md +164 -0
- package/benchmarks/results/2026-06-17-cost-verification.md +93 -0
- package/benchmarks/results/2026-06-18-agentic.md +219 -0
- package/benchmarks/results/2026-06-22-issue-245-217-comprehension.md +98 -0
- package/benchmarks/results/2026-10-07-agentic.md +186 -0
- package/cli/index.mjs +241 -0
- package/cli/lib/banner.mjs +21 -0
- package/cli/lib/install.mjs +498 -0
- package/cli/lib/settings.mjs +48 -0
- package/cli/package-lock.json +87 -0
- package/cli/package.json +42 -0
- package/cli/scripts/smoke-test.mjs +95 -0
- package/cli/scripts/smoke-worker.mjs +411 -0
- package/cli/scripts/sync-skills.mjs +81 -0
- package/commands/ponytail-audit.toml +2 -0
- package/commands/ponytail-debt.toml +2 -0
- package/commands/ponytail-gain.toml +2 -0
- package/commands/ponytail-help.toml +2 -0
- package/commands/ponytail-review.toml +2 -0
- package/commands/ponytail.toml +2 -0
- package/examples/email-validation.md +156 -0
- package/examples/group-by.md +35 -0
- package/examples/infinite-scroll.md +58 -0
- package/examples/number-formatting.md +37 -0
- package/examples/rate-limit.md +272 -0
- package/examples/react-countdown.md +390 -0
- package/examples/url-params.md +41 -0
- package/gemini-extension.json +2 -2
- package/hooks/band/board.ts +560 -0
- package/hooks/band/logo.ts +3 -0
- package/hooks/band/metrics.ts +311 -0
- package/hooks/band/register.tsx +495 -0
- package/hooks/band/types.d.ts +34 -0
- package/hooks/chill-statusline.ps1 +1 -1
- package/hooks/claude-band.json +6 -0
- package/hooks/claude-codex-hooks.json +34 -4
- package/hooks/copilot-hooks.json +12 -2
- package/hooks/cursor-hooks.json +2 -2
- package/hooks/ponytail-activate.js +201 -0
- package/hooks/ponytail-band-import.js +274 -0
- package/hooks/ponytail-config.js +175 -0
- package/hooks/ponytail-instructions.js +98 -0
- package/hooks/ponytail-map.js +111 -0
- package/hooks/ponytail-mode-tracker.js +216 -0
- package/hooks/ponytail-runtime.js +182 -0
- package/hooks/ponytail-statusline.ps1 +24 -0
- package/hooks/ponytail-statusline.sh +18 -0
- package/hooks/ponytail-subagent.js +94 -0
- package/hooks/qoder-hooks.json +19 -4
- package/i18n/README.ko.md +230 -0
- package/package.json +17 -6
- package/pi-extension/index.js +254 -0
- package/pi-extension/package.json +8 -0
- package/pi-extension/test/extension.test.js +367 -0
- package/pi-extension/test/helpers.test.js +179 -0
- package/plugin.yaml +21 -12
- package/rules/antislop.mdc +18 -0
- package/scripts/build-openclaw-skills.js +60 -0
- package/scripts/check-repo.mjs +389 -0
- package/scripts/check-rule-copies.js +74 -0
- package/scripts/check-versions.js +77 -0
- package/scripts/cursor-hooks.js +145 -0
- package/scripts/generate-chillslop.mjs +42 -0
- package/scripts/generate-examples.mjs +149 -0
- package/scripts/generate-plugin-doors.mjs +108 -0
- package/scripts/publish-openclaw-skills.js +75 -0
- package/scripts/skill-index.mjs +81 -0
- package/scripts/sync-ponytail.mjs +65 -0
- package/scripts/uninstall.js +92 -0
- package/skills/antislop/SKILL.md +700 -0
- package/skills/antislop/VERSION +1 -0
- package/skills/antislop-code/SKILL.md +128 -0
- package/skills/antislop-copywriting/SKILL.md +407 -0
- package/skills/antislop-human/LICENSE-web-quality-skills +21 -0
- package/skills/antislop-human/SKILL.md +204 -0
- package/skills/antislop-human/contrast-check.py +168 -0
- package/skills/antislop-human/contrast-mcp.py +121 -0
- package/skills/antislop-human/references/A11Y-PATTERNS.md +249 -0
- package/skills/antislop-human/references/WCAG.md +127 -0
- package/skills/antislop-layoutmobile/SKILL.md +170 -0
- package/skills/antislop-ui/LICENSE-web-quality-skills +21 -0
- package/skills/antislop-ui/SKILL.md +344 -0
- package/skills/antislop-ui/references/CORE-WEB-VITALS.md +181 -0
- package/skills/antislop-ui/references/WEB-BASELINE.md +28 -0
- package/skills/plugin.json +5 -0
- package/skills/ponytail/SKILL.md +52 -0
- package/skills/ponytail-audit/SKILL.md +103 -0
- package/skills/ponytail-debt/SKILL.md +44 -0
- package/skills/ponytail-gain/SKILL.md +52 -0
- package/skills/ponytail-help/SKILL.md +76 -0
- package/skills/ponytail-review/SKILL.md +101 -0
- package/src/package.json +3 -0
- package/tests/activation-map.test.js +63 -0
- package/tests/band-import.test.js +111 -0
- package/tests/band.test.tsx +520 -0
- package/tests/behavior.test.js +80 -0
- package/tests/commands.test.js +94 -0
- package/tests/copilot-plugin.test.js +35 -0
- package/tests/correctness.test.js +245 -0
- package/tests/cursor-hooks.test.js +343 -0
- package/tests/gemini-extension.test.js +91 -0
- package/tests/grok-plugin.test.js +39 -0
- package/tests/hermes-plugin.test.js +403 -0
- package/tests/hooks-windows.test.js +205 -0
- package/tests/hooks.test.js +1005 -0
- package/tests/kimi-plugin.test.js +25 -0
- package/tests/loc.test.js +18 -0
- package/tests/map.test.js +30 -0
- package/tests/openclaw-skills.test.js +26 -0
- package/tests/opencode-plugin.test.js +340 -0
- package/tests/package-scripts.test.js +35 -0
- package/tests/package.test.js +23 -0
- package/tests/qoder-plugin.test.js +84 -0
- package/tests/uninstall.test.js +202 -0
- /package/bin/{chill-agent.js → chill-agent.mjs} +0 -0
- /package/tests/{manifests.test.js → manifests.test.mjs} +0 -0
- /package/tests/{rules.test.js → rules.test.mjs} +0 -0
package/__init__.py
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"""Hermes plugin for Ponytail."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Callable
|
|
10
|
+
|
|
11
|
+
DEFAULT_MODE = "full"
|
|
12
|
+
RUNTIME_MODES = {"off", "lite", "full", "ultra"}
|
|
13
|
+
SKILL_COMMANDS = {
|
|
14
|
+
"ponytail-review": "Review the current diff or provided target: bugs, security, load, missing tests, speed, and what to cut.",
|
|
15
|
+
"ponytail-audit": "Audit the whole repo: bugs, security, load, missing tests, speed, and what to cut.",
|
|
16
|
+
"ponytail-debt": "List every deliberate `shortcut:` comment and its upgrade path.",
|
|
17
|
+
"ponytail-gain": "Show the measured-impact scoreboard (less code, less cost, more speed).",
|
|
18
|
+
"ponytail-help": "Show the Ponytail command reference.",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
ROOT = Path(__file__).resolve().parent
|
|
22
|
+
SKILLS_DIR = ROOT / "skills"
|
|
23
|
+
PONYTAIL_SKILL = SKILLS_DIR / "ponytail" / "SKILL.md"
|
|
24
|
+
|
|
25
|
+
_current_mode = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _normalize_runtime_mode(mode: str | None) -> str | None:
|
|
29
|
+
if not isinstance(mode, str):
|
|
30
|
+
return None
|
|
31
|
+
mode = mode.strip().lower()
|
|
32
|
+
return mode if mode in RUNTIME_MODES else None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _config_dir() -> Path:
|
|
36
|
+
if os.environ.get("XDG_CONFIG_HOME"):
|
|
37
|
+
return Path(os.environ["XDG_CONFIG_HOME"]) / "ponytail"
|
|
38
|
+
if os.name == "nt":
|
|
39
|
+
return Path(os.environ.get("APPDATA", Path.home() / "AppData" / "Roaming")) / "ponytail"
|
|
40
|
+
return Path.home() / ".config" / "ponytail"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _default_mode() -> str:
|
|
44
|
+
env_mode = _normalize_runtime_mode(os.environ.get("PONYTAIL_DEFAULT_MODE"))
|
|
45
|
+
if env_mode:
|
|
46
|
+
return env_mode
|
|
47
|
+
try:
|
|
48
|
+
data = json.loads((_config_dir() / "config.json").read_text(encoding="utf-8-sig"))
|
|
49
|
+
file_mode = _normalize_runtime_mode(data.get("defaultMode"))
|
|
50
|
+
if file_mode:
|
|
51
|
+
return file_mode
|
|
52
|
+
except Exception:
|
|
53
|
+
pass
|
|
54
|
+
return DEFAULT_MODE
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _strip_frontmatter(text: str) -> str:
|
|
58
|
+
return re.sub(r"^---[\s\S]*?---\s*", "", text or "", count=1)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _filter_skill_body_for_mode(body: str, mode: str) -> str:
|
|
62
|
+
effective = _normalize_runtime_mode(mode) or DEFAULT_MODE
|
|
63
|
+
lines = []
|
|
64
|
+
# str.splitlines() drops a trailing line terminator instead of yielding a
|
|
65
|
+
# trailing empty element, so a body ending in "\n" loses that newline on
|
|
66
|
+
# rejoin. re.split keeps it, matching the JS filter's split(/\r?\n/).
|
|
67
|
+
for line in re.split(r"\r?\n", _strip_frontmatter(body)):
|
|
68
|
+
table_label = re.match(r"^\|\s*\*\*(.+?)\*\*\s*\|", line)
|
|
69
|
+
if table_label:
|
|
70
|
+
label_mode = _normalize_runtime_mode(table_label.group(1))
|
|
71
|
+
if label_mode and label_mode != effective:
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
# Require a quoted value: every worked example is `- lite: "..."`. Without
|
|
75
|
+
# this, an ordinary rule bullet that happens to start with a mode word
|
|
76
|
+
# (e.g. "- Full: ...") is silently dropped in every other mode — it looks
|
|
77
|
+
# like a worked example but is really prose meant to survive verbatim.
|
|
78
|
+
# Mirrors the fix in hooks/ponytail-instructions.js (#571).
|
|
79
|
+
example_label = re.match(r'^-\s*([^:]+):\s*"', line)
|
|
80
|
+
if example_label:
|
|
81
|
+
label_mode = _normalize_runtime_mode(example_label.group(1))
|
|
82
|
+
if label_mode and label_mode != effective:
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
lines.append(line)
|
|
86
|
+
return "\n".join(lines)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _fallback_instructions(mode: str) -> str:
|
|
90
|
+
return (
|
|
91
|
+
f"PONYTAIL MODE ACTIVE — level: {mode}\n\n"
|
|
92
|
+
"You are a lazy senior developer. The best code is the code never written. "
|
|
93
|
+
"Before you write, list every place your change must reach (callers, tests, "
|
|
94
|
+
"fixtures) and what it could break for users. First rung that works: skip what "
|
|
95
|
+
"is not needed, reuse what the codebase has, stdlib or platform, installed "
|
|
96
|
+
"dependency, one readable line, then minimum code. No unrequested abstractions, "
|
|
97
|
+
"wrappers, options or boilerplate. Never cut trust-boundary validation, "
|
|
98
|
+
"data-loss handling, security, accessibility or anything asked for."
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def build_injected_context(mode: str | None = None) -> str:
|
|
103
|
+
"""Return the mode-filtered Ponytail context injected before LLM turns."""
|
|
104
|
+
effective = _normalize_runtime_mode(mode) or _default_mode()
|
|
105
|
+
if effective == "off":
|
|
106
|
+
return ""
|
|
107
|
+
try:
|
|
108
|
+
body = PONYTAIL_SKILL.read_text(encoding="utf-8")
|
|
109
|
+
return f"PONYTAIL MODE ACTIVE — level: {effective}\n\n{_filter_skill_body_for_mode(body, effective)}"
|
|
110
|
+
except OSError:
|
|
111
|
+
return _fallback_instructions(effective)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _pre_llm_call(
|
|
115
|
+
session_id: str = "", conversation_history: Any = None, **_: Any
|
|
116
|
+
) -> dict[str, str] | None:
|
|
117
|
+
mode = _current_mode or _default_mode()
|
|
118
|
+
# Off is injected too: the ruleset from an earlier turn is replayed every turn,
|
|
119
|
+
# and the /ponytail off reply goes to the user, never to the model.
|
|
120
|
+
context = build_injected_context(mode) or "PONYTAIL MODE OFF"
|
|
121
|
+
|
|
122
|
+
# Hermes persists hook context in api_content for cache-stable replay.
|
|
123
|
+
# Re-inject only when the active mode changed or compaction removed it.
|
|
124
|
+
for message in reversed(conversation_history or []):
|
|
125
|
+
if not isinstance(message, dict):
|
|
126
|
+
continue
|
|
127
|
+
api_content = message.get("api_content")
|
|
128
|
+
if not isinstance(api_content, str):
|
|
129
|
+
continue
|
|
130
|
+
matches = re.findall(r"PONYTAIL MODE (?:ACTIVE — level: ([a-z]+)|OFF)", api_content)
|
|
131
|
+
if matches:
|
|
132
|
+
return None if (matches[-1] or "off") == mode else {"context": context}
|
|
133
|
+
|
|
134
|
+
# Nothing persisted, so off has nothing to switch off.
|
|
135
|
+
return None if mode == "off" else {"context": context}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _skill_prompt(command: str, args: str = "") -> str:
|
|
139
|
+
tail = args.strip()
|
|
140
|
+
target = f"\n\nUser arguments: {tail}" if tail else ""
|
|
141
|
+
return (
|
|
142
|
+
f"Load and follow the Hermes plugin skill `ponytail:{command}`. "
|
|
143
|
+
f"{SKILL_COMMANDS[command]}{target}"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _slash_access_denied(event: Any, gateway: Any, command: str) -> bool:
|
|
148
|
+
if gateway is None or event is None:
|
|
149
|
+
return False
|
|
150
|
+
checker = getattr(gateway, "_check_slash_access", None)
|
|
151
|
+
source = getattr(event, "source", None)
|
|
152
|
+
if checker is None or source is None:
|
|
153
|
+
return False
|
|
154
|
+
try:
|
|
155
|
+
return checker(source, command) is not None
|
|
156
|
+
except Exception:
|
|
157
|
+
return True
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def rewrite_gateway_command(event: Any = None, gateway: Any = None, **_: Any) -> dict[str, str] | None:
|
|
161
|
+
"""Rewrite authorized gateway /ponytail-* commands into normal agent prompts."""
|
|
162
|
+
text = str(getattr(event, "text", "") or "").strip()
|
|
163
|
+
if not text.startswith("/"):
|
|
164
|
+
return None
|
|
165
|
+
head, _, rest = text[1:].partition(" ")
|
|
166
|
+
command = head.replace("_", "-").lower()
|
|
167
|
+
if command not in SKILL_COMMANDS:
|
|
168
|
+
return None
|
|
169
|
+
if _slash_access_denied(event, gateway, command):
|
|
170
|
+
return None
|
|
171
|
+
return {"action": "rewrite", "text": _skill_prompt(command, rest)}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _handle_mode_command(raw_args: str) -> str:
|
|
175
|
+
global _current_mode
|
|
176
|
+
arg = (raw_args or "").strip().lower()
|
|
177
|
+
if not arg:
|
|
178
|
+
# Bare /ponytail switches ponytail on, or reports the level when it already is (#639).
|
|
179
|
+
mode = _current_mode or _default_mode()
|
|
180
|
+
if mode != "off":
|
|
181
|
+
return f"Ponytail mode: {mode}. Use `/ponytail lite|full|ultra|off`."
|
|
182
|
+
_current_mode = "full" if _default_mode() == "off" else _default_mode()
|
|
183
|
+
return f"Ponytail mode set to {_current_mode}."
|
|
184
|
+
mode = _normalize_runtime_mode(arg)
|
|
185
|
+
if not mode:
|
|
186
|
+
return "Usage: /ponytail [lite|full|ultra|off]"
|
|
187
|
+
_current_mode = mode
|
|
188
|
+
return f"Ponytail mode set to {mode}."
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _make_skill_command_handler(ctx: Any, command: str) -> Callable[[str], str]:
|
|
192
|
+
def handler(raw_args: str) -> str:
|
|
193
|
+
prompt = _skill_prompt(command, raw_args or "")
|
|
194
|
+
injected = False
|
|
195
|
+
try:
|
|
196
|
+
injected = bool(ctx.inject_message(prompt))
|
|
197
|
+
except Exception:
|
|
198
|
+
injected = False
|
|
199
|
+
if injected:
|
|
200
|
+
return f"Queued `{command}` for the agent."
|
|
201
|
+
return prompt
|
|
202
|
+
|
|
203
|
+
return handler
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def register(ctx: Any) -> None:
|
|
207
|
+
"""Register Ponytail hooks, skills, and slash commands with Hermes."""
|
|
208
|
+
for child in sorted(SKILLS_DIR.iterdir() if SKILLS_DIR.exists() else []):
|
|
209
|
+
skill_md = child / "SKILL.md"
|
|
210
|
+
if child.is_dir() and skill_md.exists() and child.name.startswith("ponytail"):
|
|
211
|
+
ctx.register_skill(child.name, skill_md)
|
|
212
|
+
|
|
213
|
+
ctx.register_hook("pre_llm_call", _pre_llm_call)
|
|
214
|
+
ctx.register_hook("pre_gateway_dispatch", rewrite_gateway_command)
|
|
215
|
+
|
|
216
|
+
ctx.register_command(
|
|
217
|
+
"ponytail",
|
|
218
|
+
_handle_mode_command,
|
|
219
|
+
description="Set Ponytail lazy senior dev mode: lite, full, ultra, or off.",
|
|
220
|
+
args_hint="[lite|full|ultra|off]",
|
|
221
|
+
)
|
|
222
|
+
for command, description in SKILL_COMMANDS.items():
|
|
223
|
+
ctx.register_command(
|
|
224
|
+
command,
|
|
225
|
+
_make_skill_command_handler(ctx, command),
|
|
226
|
+
description=description,
|
|
227
|
+
args_hint="[target or notes]",
|
|
228
|
+
)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<svg viewBox="0 0 860 336" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system, 'Segoe UI', Helvetica, Arial, sans-serif">
|
|
2
|
+
<title>Median lines of code per arm across three models</title>
|
|
3
|
+
<text x="20" y="26" font-size="15" font-weight="600" fill="#8b949e">Median lines of code. 10 runs per cell. Lower is leaner.</text>
|
|
4
|
+
<text x="20" y="45" font-size="12" fill="#8b949e" opacity="0.85">Ponytail writes 80-94% less code, costs 42-75% less, and runs 3-6x faster than a no-skill agent.</text>
|
|
5
|
+
<rect x="20" y="58" width="12" height="12" rx="2" fill="#8b949e"/><text x="38" y="69" font-size="13" fill="#8b949e">baseline (no skill)</text>
|
|
6
|
+
<rect x="190" y="58" width="12" height="12" rx="2" fill="#d9822b"/><text x="208" y="69" font-size="13" fill="#8b949e">caveman</text>
|
|
7
|
+
<rect x="300" y="58" width="12" height="12" rx="2" fill="#2da44e"/><text x="318" y="69" font-size="13" fill="#8b949e">ponytail</text>
|
|
8
|
+
<text x="112" y="119" font-size="13" font-weight="600" fill="#8b949e" text-anchor="end">Haiku</text>
|
|
9
|
+
<rect x="120" y="92" width="508" height="14" rx="2" fill="#8b949e"/><text x="634" y="103" font-size="11" fill="#8b949e">518</text>
|
|
10
|
+
<rect x="120" y="110" width="114" height="14" rx="2" fill="#d9822b"/><text x="240" y="121" font-size="11" fill="#d9822b">116</text>
|
|
11
|
+
<rect x="120" y="128" width="38" height="14" rx="2" fill="#2da44e"/><text x="164" y="139" font-size="11" fill="#2da44e" font-weight="600">39</text>
|
|
12
|
+
<text x="112" y="193" font-size="13" font-weight="600" fill="#8b949e" text-anchor="end">Sonnet</text>
|
|
13
|
+
<rect x="120" y="166" width="680" height="14" rx="2" fill="#8b949e"/><text x="806" y="177" font-size="11" fill="#8b949e">693</text>
|
|
14
|
+
<rect x="120" y="184" width="118" height="14" rx="2" fill="#d9822b"/><text x="244" y="195" font-size="11" fill="#d9822b">120</text>
|
|
15
|
+
<rect x="120" y="202" width="43" height="14" rx="2" fill="#2da44e"/><text x="169" y="213" font-size="11" fill="#2da44e" font-weight="600">44</text>
|
|
16
|
+
<text x="112" y="267" font-size="13" font-weight="600" fill="#8b949e" text-anchor="end">Opus</text>
|
|
17
|
+
<rect x="120" y="240" width="251" height="14" rx="2" fill="#8b949e"/><text x="377" y="251" font-size="11" fill="#8b949e">256</text>
|
|
18
|
+
<rect x="120" y="258" width="66" height="14" rx="2" fill="#d9822b"/><text x="192" y="269" font-size="11" fill="#d9822b">67</text>
|
|
19
|
+
<rect x="120" y="276" width="50" height="14" rx="2" fill="#2da44e"/><text x="176" y="287" font-size="11" fill="#2da44e" font-weight="600">51</text>
|
|
20
|
+
<text x="120" y="324" font-size="11" fill="#8b949e" opacity="0.8">Median of 10 runs/cell, default temperature. 5 tasks (email, debounce, CSV sum, countdown, rate-limit), same model per group. Reproduce: npx promptfoo eval -c benchmarks/promptfooconfig.yaml</text>
|
|
21
|
+
</svg>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<svg viewBox="0 0 860 488" xmlns="http://www.w3.org/2000/svg" font-family="-apple-system, 'Segoe UI', Helvetica, Arial, sans-serif">
|
|
2
|
+
<title>Each arm vs the no-skill baseline across every metric, plus safety, Claude Code on Haiku 4.5</title>
|
|
3
|
+
<text x="430" y="24" font-size="15" font-weight="600" fill="#8b949e" text-anchor="middle">Every metric vs the no-skill baseline (Claude Code, Haiku 4.5, 12 tasks)</text>
|
|
4
|
+
|
|
5
|
+
<rect x="212" y="38" width="12" height="12" rx="2" fill="#8b949e"/><text x="229" y="48" font-size="12" fill="#8b949e">baseline</text>
|
|
6
|
+
<rect x="300" y="38" width="12" height="12" rx="2" fill="#d9822b"/><text x="317" y="48" font-size="12" fill="#8b949e">caveman</text>
|
|
7
|
+
<rect x="392" y="38" width="12" height="12" rx="2" fill="#2da44e"/><text x="409" y="48" font-size="12" fill="#8b949e">ponytail</text>
|
|
8
|
+
<rect x="478" y="38" width="12" height="12" rx="2" fill="#8957e5"/><text x="495" y="48" font-size="12" fill="#8b949e">yagni-oneliner</text>
|
|
9
|
+
|
|
10
|
+
<text x="32" y="248" font-size="12" fill="#8b949e" text-anchor="middle" transform="rotate(-90 32 248)">% of baseline (lower is leaner)</text>
|
|
11
|
+
<line x1="85" y1="360" x2="815" y2="360" stroke="#8b949e" stroke-opacity="0.55"/>
|
|
12
|
+
<line x1="85" y1="305" x2="815" y2="305" stroke="#8b949e" stroke-opacity="0.16"/>
|
|
13
|
+
<line x1="85" y1="250" x2="815" y2="250" stroke="#8b949e" stroke-opacity="0.16"/>
|
|
14
|
+
<line x1="85" y1="195" x2="815" y2="195" stroke="#8b949e" stroke-opacity="0.16"/>
|
|
15
|
+
<line x1="85" y1="140" x2="815" y2="140" stroke="#8b949e" stroke-opacity="0.45" stroke-dasharray="4 4"/>
|
|
16
|
+
<text x="78" y="364" font-size="11" fill="#8b949e" text-anchor="end">0%</text>
|
|
17
|
+
<text x="78" y="309" font-size="11" fill="#8b949e" text-anchor="end">25%</text>
|
|
18
|
+
<text x="78" y="254" font-size="11" fill="#8b949e" text-anchor="end">50%</text>
|
|
19
|
+
<text x="78" y="199" font-size="11" fill="#8b949e" text-anchor="end">75%</text>
|
|
20
|
+
<text x="78" y="144" font-size="11" fill="#8b949e" text-anchor="end">100%</text>
|
|
21
|
+
|
|
22
|
+
<!-- LOC -->
|
|
23
|
+
<rect x="108" y="140" width="30" height="220" rx="2" fill="#8b949e"/><text x="123" y="135" font-size="10" fill="#8b949e" text-anchor="middle">100%</text>
|
|
24
|
+
<rect x="146" y="184" width="30" height="176" rx="2" fill="#d9822b"/><text x="161" y="179" font-size="10" fill="#d9822b" text-anchor="middle">80%</text>
|
|
25
|
+
<rect x="184" y="259" width="30" height="101" rx="2" fill="#2da44e"/><text x="199" y="254" font-size="10" font-weight="600" fill="#2da44e" text-anchor="middle">46%</text>
|
|
26
|
+
<rect x="222" y="213" width="30" height="147" rx="2" fill="#8957e5"/><text x="237" y="208" font-size="10" fill="#8957e5" text-anchor="middle">67%</text>
|
|
27
|
+
<text x="180" y="380" font-size="13" fill="#8b949e" text-anchor="middle">LOC</text>
|
|
28
|
+
<text x="180" y="395" font-size="10" fill="#8b949e" opacity="0.8" text-anchor="middle">base 191</text>
|
|
29
|
+
|
|
30
|
+
<!-- tokens -->
|
|
31
|
+
<rect x="288" y="140" width="30" height="220" rx="2" fill="#8b949e"/><text x="303" y="135" font-size="10" fill="#8b949e" text-anchor="middle">100%</text>
|
|
32
|
+
<rect x="326" y="125" width="30" height="235" rx="2" fill="#d9822b"/><text x="341" y="120" font-size="10" fill="#d9822b" text-anchor="middle">107%</text>
|
|
33
|
+
<rect x="364" y="188" width="30" height="172" rx="2" fill="#2da44e"/><text x="379" y="183" font-size="10" font-weight="600" fill="#2da44e" text-anchor="middle">78%</text>
|
|
34
|
+
<rect x="402" y="171" width="30" height="189" rx="2" fill="#8957e5"/><text x="417" y="166" font-size="10" fill="#8957e5" text-anchor="middle">86%</text>
|
|
35
|
+
<text x="360" y="380" font-size="13" fill="#8b949e" text-anchor="middle">tokens</text>
|
|
36
|
+
<text x="360" y="395" font-size="10" fill="#8b949e" opacity="0.8" text-anchor="middle">base 349k</text>
|
|
37
|
+
|
|
38
|
+
<!-- cost -->
|
|
39
|
+
<rect x="468" y="140" width="30" height="220" rx="2" fill="#8b949e"/><text x="483" y="135" font-size="10" fill="#8b949e" text-anchor="middle">100%</text>
|
|
40
|
+
<rect x="506" y="136" width="30" height="224" rx="2" fill="#d9822b"/><text x="521" y="131" font-size="10" fill="#d9822b" text-anchor="middle">102%</text>
|
|
41
|
+
<rect x="544" y="184" width="30" height="176" rx="2" fill="#2da44e"/><text x="559" y="179" font-size="10" font-weight="600" fill="#2da44e" text-anchor="middle">80%</text>
|
|
42
|
+
<rect x="582" y="188" width="30" height="172" rx="2" fill="#8957e5"/><text x="597" y="183" font-size="10" fill="#8957e5" text-anchor="middle">78%</text>
|
|
43
|
+
<text x="540" y="380" font-size="13" fill="#8b949e" text-anchor="middle">cost</text>
|
|
44
|
+
<text x="540" y="395" font-size="10" fill="#8b949e" opacity="0.8" text-anchor="middle">base $0.10</text>
|
|
45
|
+
|
|
46
|
+
<!-- time -->
|
|
47
|
+
<rect x="648" y="140" width="30" height="220" rx="2" fill="#8b949e"/><text x="663" y="135" font-size="10" fill="#8b949e" text-anchor="middle">100%</text>
|
|
48
|
+
<rect x="686" y="136" width="30" height="224" rx="2" fill="#d9822b"/><text x="701" y="131" font-size="10" fill="#d9822b" text-anchor="middle">102%</text>
|
|
49
|
+
<rect x="724" y="199" width="30" height="161" rx="2" fill="#2da44e"/><text x="739" y="194" font-size="10" font-weight="600" fill="#2da44e" text-anchor="middle">73%</text>
|
|
50
|
+
<rect x="762" y="206" width="30" height="154" rx="2" fill="#8957e5"/><text x="777" y="201" font-size="10" fill="#8957e5" text-anchor="middle">70%</text>
|
|
51
|
+
<text x="720" y="380" font-size="13" fill="#8b949e" text-anchor="middle">time</text>
|
|
52
|
+
<text x="720" y="395" font-size="10" fill="#8b949e" opacity="0.8" text-anchor="middle">base 69s</text>
|
|
53
|
+
|
|
54
|
+
<text x="20" y="418" font-size="11" fill="#8b949e" opacity="0.8">Each bar = that arm's mean as a % of the no-skill baseline (the gray 100% bars). Lower is leaner / cheaper / faster; caveman rises above 100% on tokens, cost and time. n=4.</text>
|
|
55
|
+
|
|
56
|
+
<line x1="20" y1="438" x2="815" y2="438" stroke="#8b949e" stroke-opacity="0.25"/>
|
|
57
|
+
<text x="20" y="460" font-size="11" fill="#8b949e" opacity="0.9">Safety, separate 6-task adversarial tier (path-traversal, SQLi, token forgery, malformed input, rate-limit). Higher is safer:</text>
|
|
58
|
+
<text x="90" y="478" font-size="12" fill="#8b949e">baseline 100%</text>
|
|
59
|
+
<text x="230" y="478" font-size="12" fill="#d9822b">caveman 100%</text>
|
|
60
|
+
<text x="370" y="478" font-size="12" font-weight="600" fill="#2da44e">ponytail 100%</text>
|
|
61
|
+
<text x="510" y="478" font-size="12" fill="#8957e5">yagni-oneliner <tspan fill="#cf222e" font-weight="600">95%</tspan> (dropped a guard once)</text>
|
|
62
|
+
</svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|