210 Commits

Author SHA1 Message Date
Peter 2ed6c52c9d feat: add Grok Build native skills adapter (revive #561) (#661)
* feat: add Grok Build adapter (revive #561 on current main)

Thin Grok packaging under .grok-plugin/ with root plugin.json path
overrides (hooks + MCP). SessionStart/UserPromptSubmit/SubagentStart
reuse shared hooks/ponytail-*.js; mode state under GROK_PLUGIN_DATA.

Rebases the approach from #561 onto current main: keep Qoder detection
and output paths, add isGrok, export getGrokPluginDataDir, drop bash-only
exec from Grok hooks, and document install/enable/uninstall on the
front-page README (en/es/ko) plus agent-portability.

Direct install works today:
  grok plugin install DietrichGebert/ponytail --trust

Marketplace root source ("./") matches Claude; Grok's scanner still
rejects it (see xai-org/plugin-marketplace#123 class of bugs).

Co-authored-by: Vinícius Souza <souza.vinicius@bb.com.br>

* fix(grok): drop MCP, harden host detection and tests

Review feedback on #661:
- Remove MCP wiring (git install never installs ponytail-mcp deps; no
  other host ships MCP; hooks+skills cover always-on)
- Drop static plugin-index.json (optional catalog fluff)
- Clear GROK_PLUGIN_* in hooks.test.js so host suites cannot leak
- Exclusive isGrok after Copilot/Codex; state falls back to ROOT not ~/.claude
- Tighten Qoder regression assert; structural checks for plugin.json/hooks
- List Grok Build among skill-capable hosts in README

* refactor(grok): DRY — reuse Claude/Codex hooks map

Second review pass for #661:

- Delete .grok-plugin/hooks.json (near-copy of claude-codex-hooks.json).
  Root plugin.json points at the shared map; Grok sets CLAUDE_PLUGIN_ROOT.
- Drop getGrokPluginDataDir; inline GROK_PLUGIN_DATA || ROOT like other hosts.
- Grok uses Claude-compatible writeHookOutput (raw SessionStart, JSON
  SubagentStart) instead of a separate raw-only branch.
- Slim .grok-plugin/marketplace.json to match .claude-plugin.
- Tests: shared-map assert, SubagentStart JSON under Grok, Qoder isolation.

* fix(grok): use native skill activation

* chore: drop unrelated Qoder formatting

---------

Co-authored-by: Vinícius Souza <souza.vinicius@bb.com.br>
2026-08-08 00:44:01 +03:00
krish rathi a2712bc830 fix: detect VS Code Copilot via CLAUDE_PLUGIN_ROOT fallback (#528) (#579)
VS Code Copilot never sets COPILOT_PLUGIN_DATA, only CLAUDE_PLUGIN_ROOT
(pointed at an .vscode/agent-plugins/... install path), so isCopilot was
false and ponytail assumed native Claude Code — emitting the statusline
setup nudge, which VS Code Copilot doesn't read. Also guard stateDir:
it was built from the same unset COPILOT_PLUGIN_DATA whenever isCopilot
resolved via this new fallback, so fall back to getClaudeDir() there too.
2026-08-08 00:37:09 +03:00
Prayag Raj cc37a5d581 fix: drop commandWindows from hooks.json for Claude.ai marketplace validation (#593) (#601)
commandWindows is not part of the supported hooks schema for the Claude.ai
plugin marketplace validator, which rejects it as an unknown field. The
shared `command` field already runs cross-platform: Claude Code expands
${CLAUDE_PLUGIN_ROOT} before the shell sees it, VS Code Copilot ignores
commandWindows and runs `command` through PowerShell on Windows, and `node`
is natively available in both bash and PowerShell.

Update the regression test to assert commandWindows is absent and remove
the now-unused CMD_VAR_SYNTAX guard. Fix comment placeholder syntax
({{CLAUDE_PLUGIN_ROOT}} → ${CLAUDE_PLUGIN_ROOT}) and narrow test name.
2026-08-08 00:34:41 +03:00
DietrichGebert 0a4dd63ad4 chore: release v4.9.0 (#703)
Co-authored-by: Dietrich Gebert <dgebert@Dietrichs-MacBook-Pro.local>
v4.9.0
2026-08-08 00:13:25 +03:00
DietrichGebert 16f29800fd docs: FAQ entry on using caveman with ponytail (#599)
test / test (push) Has been cancelled
Most-asked question, and the answer was only ever in the skill file
(skills/ponytail/SKILL.md: "pair with Caveman for terse prose"), never
in the README. Added to both READMEs as the first FAQ entry.

Co-authored-by: Dietrich Gebert <dgebert@Dietrichs-MacBook-Pro.local>
2026-07-15 23:32:15 +02:00
gglucass 14a0d79548 Emit statusline setup nudge at most once per user (#483)
Writes a flag file the first time the STATUSLINE SETUP NEEDED nudge fires, and skips it on later sessions so it stops nagging every start. Includes a regression test that a second session stays silent.
2026-07-10 04:44:32 +02:00
Cody Jung f12f210eb5 fix(benchmarks): kill timed-out agent cells cross-platform (#225)
run_cell's timeout handler called `taskkill /F /T`, a Windows-only command, with
no POSIX branch. On macOS/Linux a timed-out cell raised FileNotFoundError instead
of being killed, leaking the agent's process tree and logging the cell as an error
rather than a clean timeout-kill.

Branch the kill on platform: taskkill on Windows, os.killpg on POSIX. The cell now
starts in its own session (Popen start_new_session) so its process group is
killable. A _selftest_kill check (run.py --selftest) spawns a sleeper and asserts
the tree-kill terminates it on whatever platform CI runs.
2026-07-10 03:24:23 +02:00
Dhruv Garg 0cdd11fe0c fix: stop filterSkillBodyForMode from swallowing rule bullets that start with a mode word (#571)
filterSkillBodyForMode is the shared filter every host adapter (Claude
hooks, Pi, OpenCode, the MCP server, Hermes) uses to trim the ponytail
skill body down to the active intensity. Its worked-example detection
matched any bullet shaped "- label: text" and treated it as a
mode-specific example whenever the label happened to normalize to a
mode name (lite/full/ultra/off) -- silently dropping it in every other
mode.

The current SKILL.md doesn't trip this today (its only such bullets are
the three real worked examples), but it's a landmine: an ordinary rule
bullet added later that happens to start with "Full:" or "Lite:" would
vanish from every other mode's instructions with no warning, in the one
code path every adapter shares. Confirmed with a real bullet:

  filterSkillBodyForMode('- Full: real rule text.', 'ultra')

silently drops the line before this fix.

All three real worked examples are quoted (`- lite: "..."`); ordinary
rule bullets aren't. Required the quote so only genuine examples are
mode-filtered, and added a regression test plus fixed an existing test
whose synthetic fixture used unquoted examples that don't match the
real file's format.
2026-07-10 03:04:06 +02:00
DietrichGebert b6c04480c0 fix: narrow the ponytail: marker to real corner-cuts, keep the prefix (#120) (#577)
The ponytail: comment marker was being over-applied to trivial code (#120).
Narrow the rule so only deliberate simplifications that cut a real corner with a
known ceiling get a comment, and keep the `ponytail:` prefix that /ponytail-debt
greps for. Applied across all rule copies, SKILL.md, AGENTS.md, the fallback
instructions, both command files, and the Qoder/OpenCode copies that #491
missed; .openclaw regenerated. helpers.test.js now pins the prefix so it cannot
silently drop again.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 02:52:02 +02:00
DietrichGebert 65db9025a7 fix: reject review as a default mode in pi-extension and config (#576)
review is a session-only mode set by /ponytail-review, never a persistent
default. The Claude/Codex mode-tracker already enforces this (#377), but two
paths did not:

- pi-extension /ponytail default review was accepted, persisting review as the
  default, so the next session started in review with a blank status-bar icon.
- getDefaultMode accepted review from PONYTAIL_DEFAULT_MODE or a stale config.

Route default validation through RUNTIME_MODES (off/lite/full/ultra) in
getDefaultMode, writeDefaultMode, and the pi-extension default command. review
stays valid as a session mode. Adds regression tests for all three paths.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 01:48:24 +02:00
saime428 3465b1a3ca Fix Codex hook output schema (#573) (#574)
Restore hookSpecificOutput so strict Codex schemas accept SessionStart, UserPromptSubmit, and SubagentStart context.

Co-authored-by: saime <2286263079@qq.com>
2026-07-10 01:16:30 +02:00
Sandeep pandey 2ba0262111 fix: drop bash-only exec from hooks so they run under PowerShell (#527, #569) (#572)
The three shared `command` fields in claude-codex-hooks.json used
`exec node "..."`. `exec` is a bash/zsh builtin with no PowerShell
equivalent. Some hosts run the `command` field through PowerShell on
Windows regardless of the PowerShell-native `commandWindows` field:
VS Code Copilot always does (it never reads `commandWindows`), and
native Claude Code launched from Git Bash was reported doing the same
(#569). PowerShell then fails on the first token with
CommandNotFoundException, so every SessionStart / SubagentStart /
UserPromptSubmit hook errors out on Windows.

Plain `node "..."` runs natively in both bash and PowerShell. Dropping
`exec` only gives up the POSIX optimization of replacing the wrapper
shell with node; the wrapper-process pileup that #461 added `exec` to
avoid is already handled by each hook's stdin self-exit guard
(#443/#477), so there is no regression on Codex/zsh.

Updated the hooks-windows regression test to assert the shell-agnostic
form instead of requiring `exec`.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 01:06:07 +02:00
nanaubusiness 523e9dc051 fix: read version from package.json instead of hardcoding 0.1.0 (#354)
Closes #345

Co-authored-by: Admin <admin@Admins-MacBook-Pro.local>
2026-07-09 18:06:25 +02:00
Aly Dhedhi 33c00d3d7a fix: Codex CLI SessionStart additionalContext at top level (#505) (#508)
* fix: Codex CLI SessionStart additionalContext at top level (#505)

Codex CLI expects additionalContext at the top level of the hook output,
not nested under hookSpecificOutput. Flattened the Codex branch of
writeHookOutput so the injected instructions actually reach the agent.

Fixes #505

* Merge main; move #99 Codex @ponytail report assertion to top-level additionalContext (#505)

---------

Co-authored-by: Emeriko <dietrich.gebert@gmail.com>
2026-07-09 18:03:43 +02:00
guyoron1 055a1453d3 fix: merge system entries in OpenCode plugin for Qwen compat (#296) (#536)
Qwen models reject multiple system messages — push() added a second
entry.  Append to the last existing entry instead; fall back to push
when the array is empty.

Co-authored-by: Emeriko <dietrich.gebert@gmail.com>
2026-07-09 17:53:06 +02:00
guyoron1 fee48ca59a fix: guard against non-string input.arguments in OpenCode plugin (#557)
Wrap input.arguments with String() before calling .trim(), matching
the defensive pattern already used in the pi-extension (index.js:90).
Prevents TypeError if the host passes a non-string truthy value.

Fixes #553.

Co-authored-by: Emeriko <dietrich.gebert@gmail.com>
2026-07-09 17:52:22 +02:00
guyoron1 8555a0a580 fix: catch fs errors when writing default mode in pi-extension (#559)
writeDefaultMode() calls mkdirSync/writeFileSync which can throw on
permission errors or disk-full. Wrap in try-catch so the user gets
a notification instead of an unhandled promise rejection.

Fixes #555.
2026-07-09 17:43:37 +02:00
Anders Brams 106c0a47a6 Revise Ponytail plugin installation steps (#545)
Updated installation instructions for Ponytail plugin.

Co-authored-by: Emeriko <dietrich.gebert@gmail.com>
2026-07-09 17:39:47 +02:00
Hrqz 6165b7011a fix(pi-extension): guard theme access with try-catch for pi-web compatibility (#336)
pi-coding-agent 0.80.x uses a Proxy for the theme object that throws
'Theme not initialized' when accessed before initTheme(). In pi-web,
the session_start event fires before the theme is ready, causing
the optional chaining guard to fail (Proxy is truthy, ?. can't
catch Proxy get-trap throws).

Wrap theme access in try-catch so the extension degrades gracefully.

Co-authored-by: huantian666 <huantian666@gmail.com>
Co-authored-by: Emeriko <dietrich.gebert@gmail.com>
2026-07-09 17:27:28 +02:00
krish rathi 1715abccac feat(hooks): opt-in agent-type scoping for SubagentStart injection (#… (#522)
* feat(hooks): opt-in agent-type scoping for SubagentStart injection (#506)

The SubagentStart hook injected the ~1,300-token persona into every
subagent, including read-only search agents where it is dead weight.
Consumers could not narrow a plugin-provided hook from their own
settings, so the scoping has to live here.

Add an opt-in allowlist: PONYTAIL_SUBAGENT_AGENTS (comma-separated env
var) or a subagentAgents array in config.json, matching getDefaultMode
precedence. The hook now reads agent_type from stdin and skips
injection only on a positive mismatch; unconfigured or missing
agent_type keeps the inject-everywhere behavior from issue #252. Stdin
reading follows the never-hang contract from issue #443.

Closes #506

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Address review: regex matcher, stdin-free default path, exit on skip

Rework the SubagentStart scoping per review on #522:

- Replace the comma-separated PONYTAIL_SUBAGENT_AGENTS allowlist with a
  regex PONYTAIL_SUBAGENT_MATCHER, matching the reviewed API on #507 and
  the PONYTAIL_DEFAULT_MODE naming convention.
- Default path (no matcher) injects synchronously and never reads stdin,
  fixing the #443-class Windows regression where the persona was delayed
  until a 1s timeout.
- On a definite agent_type mismatch the hook now process.exit(0)s
  immediately instead of lingering until the fallback timer.
- Drop the config-file (subagentAgents) source, so no config read on the
  subagent hot path; env-only keeps it lazy (YAGNI).
- Revert the unrelated tests/hooks-windows.test.js change; the scoped
  path mirrors the mode-tracker never-hang contract already tested there.

Fail open on unparseable input, missing agent_type, invalid regex, or
timeout so scoping never silently drops the persona (#252).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Emeriko <dietrich.gebert@gmail.com>
2026-07-09 17:23:07 +02:00
shixiaoding 83493b97bc feat: add Qoder hooks support (UserPromptSubmit + PreToolUse) (#529)
* feat: add Qoder hooks support (UserPromptSubmit + PreToolUse)

- Add isQoder detection via QODER_SESSION_ID in ponytail-runtime.js
- Add UserPromptSubmit double-duty: auto-activate + ruleset injection
- Add PreToolUse hook with task|Task matcher for subagent injection
- Create hooks/qoder-hooks.json reference template
- Update .qoder-plugin/plugin.json with hooks field
- Add .qoder/rules/ponytail.md for per-project rules
- Add comprehensive tests for Qoder runtime behavior
- Update README.md and docs/agent-portability.md

* check-versions: enforce .qoder-plugin/plugin.json version too

---------

Co-authored-by: shiding <shiding@ruigushop.com>
Co-authored-by: Emeriko <dietrich.gebert@gmail.com>
2026-07-09 17:13:59 +02:00
Hamza Ali Shahjahan 8f32ae0f6e docs: add JetBrains Junie, Amp, and Jules support (native AGENTS.md readers) (#326)
* docs: add JetBrains Junie support (native AGENTS.md reader)

Junie reads AGENTS.md from the project root natively (per JetBrains' official docs), so ponytail works zero-config in any JetBrains IDE — like CodeWhale (#124). Adds a README note + agent-portability row, and bumps the agent badge 16 -> 17. Docs-only.

* docs: also add Amp and Jules support (native AGENTS.md readers)

Amp (Sourcegraph) reads AGENTS.md as its primary guidance file (cwd + parents up to $HOME + global config; per ampcode.com/manual). Jules (Google) auto-reads AGENTS.md from the repo root (per jules.google/docs). Both work zero-config via the AGENTS.md this repo ships. Badge 17->19. Roo Code evaluated and excluded (archived 2026-05-15).

* Merge main; correct Junie AGENTS.md note (Guidelines Path, not automatic)

---------

Co-authored-by: Emeriko <dietrich.gebert@gmail.com>
2026-07-09 12:48:17 +02:00
Philipp Metzler d5f4ffdd65 pi-extension: make startup notification opt-out via quietStartup (#308)
The status bar already shows the active ponytail mode at all times, but the
startup toast is also a deliberate trust-heartbeat for a behavior-modifying
extension (it confirms ponytail loaded, in which mode, and survives session
resume where the persisted mode may differ from the default). Default
behavior is preserved.

Users who prefer a clean startup can now silence it via:

  - config:  ~/.config/ponytail/config.json -> { "quietStartup": true }
  - env var: PONYTAIL_QUIET_STARTUP=1

Resolution order mirrors the existing defaultMode setting: env var > config
file > default (false). Added readQuietStartup export and a test covering
all three layers.

Co-authored-by: blissini <blissini@users.noreply.github.com>
Co-authored-by: Emeriko <dietrich.gebert@gmail.com>
2026-07-09 12:29:12 +02:00
Yash Yadav 9888cdf066 docs: update Claude Code Desktop install steps to match the current UI (#342) (#525)
The desktop-app note pointed at a Customize > + > "Create plugin and
add marketplace" > "Add from repository" flow (from #98) that no
longer matches the app. The unified Claude Code Desktop app's Code
tab runs real Claude Code sessions, so the same /plugin marketplace
add and /plugin install commands typed above work directly in its
prompt box; the + button also opens a Plugins browser, and
marketplaces are managed under Customize in the sidebar (per
Anthropic's current desktop app docs).

Closes #342
2026-07-09 11:52:05 +02:00
Ben Younes efa35c34f2 fix: preserve combined statusline on uninstall (#550) 2026-07-09 11:48:37 +02:00
D3VL0PR 511ddd4d62 fix(opencode): stop exporting parseCommandFile from plugin module (#301)
OpenCode's legacy plugin loader iterates every exported function in a
plugin module and tries to invoke each one with the plugin context object.
parseCommandFile was exported alongside the default plugin function, so
the loader called it with an object instead of a file path and crashed
with 'path must be a string or a file descriptor' before any hook ran,
which aborted the whole plugin load.

Move the frontmatter parser to its own .cjs sibling; the plugin module
now exposes exactly one top-level export (the default plugin function).
The plugin requires the parser via createRequire, and the existing
parseCommandFile tests now require the sibling directly.

Co-authored-by: ayushere <ayushere@users.noreply.github.com>
2026-07-09 11:44:31 +02:00
Jay shah 8e69b4a55f feat: report active level on bare /ponytail instead of resetting to default (#99)
* fix: report active level on bare /ponytail instead of resetting to default

* remove . ponytail-active

---------

Co-authored-by: Emeriko <dietrich.gebert@gmail.com>
2026-07-09 04:15:43 +02:00
JACK 6d0c111035 Fix ponytail test coverage and metadata (#503)
Co-authored-by: Jack <jack@JackdeMacBook-Pro.local>
2026-07-09 03:35:39 +02:00
Hannes 72274181e9 fix(pi-extension): list ponytail modes (#237) 2026-07-09 03:29:35 +02:00
Hamza Ali Shahjahan fb72987fb5 fix(benchmarks): make agentic fixture path portable via PONYTAIL_TMPL (#206)
tasks.py hardcoded _TMPL to a personal absolute Windows path (D:\dev\...), which the agentic benchmark's Reproduce flow depends on. Resolve it via a PONYTAIL_TMPL env override with a relative fallback, mirroring the existing PONYTAIL_PLUGIN_DIR convention in run.py; run.py's loader resolves the relative name under fixtures/. selftest skips the open tasks that consume _TMPL, so instrument validation is unaffected (verified: all instruments valid).
2026-07-09 03:13:52 +02:00
Sangeeth Thilakarathna a3e0169c3d fix: ignore invalid opencode mode arguments (#302)
Co-authored-by: sanmaxdev <sanmaxdev@users.noreply.github.com>
2026-07-09 03:12:14 +02:00
MrKC ca4dad252d test: run ponytail-mcp test suite under root npm test (#235) 2026-07-09 03:08:41 +02:00
Rafael Pinheiro bbace73bed check-rule-copies: pin the reuse rung as an invariant (#281)
The reuse rung added in #253 ("Already in this codebase? Reuse it, don't
re-write it.") is load-bearing now, but it was not in the INVARIANTS list,
so a future reword could silently drop it from SKILL.md or AGENTS.md without
tripping the check. Pin it with the substring "in this codebase", present in
both files' rung 2, the same way the safety carve-outs are pinned.

Co-authored-by: Rafaelpta <15382305+Rafaelpta@users.noreply.github.com>
2026-07-09 03:04:19 +02:00
Lakshya Sharma 187acc2add ux: make the ultra statusline badge stand out (#192)
Every mode rendered in the same green, so ultra (the high-intensity mode
that deletes and challenges requirements) looked identical to lite/full at
a glance. Give the ultra badge an amber color so it's obvious you're in it.
The level is still in the badge text, so color stays a redundant cue, not
the only one.
2026-07-09 03:01:14 +02:00
Hamza Ali Shahjahan eb3b6f0bbd docs: align benchmark prerequisites with promptfoo's Node requirement (#205)
The Prerequisites section listed Node.js 18+, but the Reproduce section (and promptfoo's engine constraint) requires >= 22.22.0, so a reader on Node 18 hits promptfoo's engine error. The local Ollama path uses no Node, so 18+ had no basis. One-line docs change aligning the prerequisite with the existing requirement above it.
2026-07-09 02:57:37 +02:00
Lakshya Sharma 01a29e480a docs: add Zed to the agent portability table (#190)
The README lists Zed as a supported instruction-tier agent and points
readers to docs/agent-portability.md for the file mapping, but the table
had no Zed row. Zed auto-includes AGENTS.md from the worktree root as a
default rule file, so add the row to match the README.
2026-07-09 02:56:12 +02:00
DietrichGebert a439cd2930 docs: reword Copilot Chat note without em dash (#526) (#562) 2026-07-09 02:54:11 +02:00
Yash Yadav a195670068 docs: clarify that GitHub Copilot Chat (VS Code/JetBrains) is supported (#497) (#526)
"GitHub Copilot (editor)" was buried in a comma-separated adapter
list with no indication of which product it meant, so readers asking
"does this support VS Code Copilot?" had no way to tell from the
README. Spell out VS Code/JetBrains/Visual Studio explicitly and
disambiguate from the separately documented Copilot CLI.

Closes #497
2026-07-09 02:39:16 +02:00
Lorenzo Sanz 6c08d36447 Update Spanish README with improved wording (#560) 2026-07-09 01:25:23 +02:00
DietrichGebert 1b2760d384 feat: hide the pi status bar indicator while keeping ponytail active (#324) (#544)
Add getHideStatus() to the shared config resolver: PONYTAIL_HIDE_STATUS (any
truthy value; 0/false/empty mean don't hide) or config.hideStatus === true, with
BOM-stripped config reads. The pi extension caches it at init and re-reads on
session_start, and syncStatus() returns early when set, so the indicator is
suppressed while the ruleset still injects every turn.

Reconciles #511 and #328: takes #328's caching and ruleset-stays-active test,
#511's BOM-safe config read, a stricter env check (0/false do not hide), and
drops #328's writeDefaultMode change which already landed in #514.

Closes #324

Co-authored-by: dhedhialy <91044156+dhedhialy@users.noreply.github.com>
Co-authored-by: sanmaxdev <46221775+sanmaxdev@users.noreply.github.com>
2026-07-07 04:06:37 +02:00
DietrichGebert bed76c52fa feat: add /ponytail default <mode> to persist the default (#329) (#541)
Claude Code had no way to change the persistent default from within a session:
plain /ponytail switches are session-scoped by design ('sticks until session
end'), and only env/config could set the default. So /ponytail off never
survived a restart, which is what #329 hit.

Add /ponytail default <mode> to the mode-tracker hook, matching pi's existing
command: it writes defaultMode to config (survives restart) and does NOT touch
the current session flag, keeping plain switches transient. review is rejected
as a default (#377); only off/lite/full/ultra are accepted. Test covers persist,
transient-switch-does-not-persist, and review-rejected.

Closes #329
2026-07-07 03:09:25 +02:00
Lakshya Sharma f790cebb57 fix: statusline setup nudge honors CLAUDE_CONFIG_DIR (#338)
The activation hook detects a missing statusline in getClaudeDir()/settings.json
(CLAUDE_CONFIG_DIR-aware) but the nudge text told the user and the agent to add
the statusLine config to ~/.claude/settings.json. With CLAUDE_CONFIG_DIR set,
that is the wrong file: Claude Code never reads it and the badge never turns on.
Point the nudge at the settingsPath the hook already computed, in both the
shell-safe and manual branches.

Fixes #250
2026-07-07 02:54:46 +02:00
Aly Dhedhi 988428d510 fix: writeDefaultMode merges config instead of overwriting (#490) (#514)
writeDefaultMode was writing a fresh { defaultMode } object, silently
dropping any other fields in config.json. Now it reads the existing config,
updates only defaultMode, and writes it back preserving all other fields.

Fixes #490
2026-07-07 02:44:56 +02:00
Aly Dhedhi 3869218b13 fix: hermes test hardcoding python3 (fails on Windows) (#513)
tests/hermes-plugin.test.js spawned python3 with no fallback. On Windows
python3 is the Store app-execution-alias stub that exits non-zero even when
Python is installed. Reuse the probe/fallback pattern from
benchmarks/correctness.js: try python3, fall back to python, cache the result.

Fixes #498
2026-07-07 02:31:15 +02:00
Aly Dhedhi 146904825d fix: handle CRLF in loc.js fence regex and remove dead codeOf (#339) (#518)
loc.js regex was missing \r? before \n, so CRLF code fences were
never matched and the metric fell back to counting the whole response.
Also removed dead codeOf() from behavior.js (defined but never called).

Fixes #339
2026-07-07 02:26:10 +02:00
DietrichGebert 3efe7376ff docs(help): add /ponytail-gain to the OpenCode help card (#539)
The OpenCode /ponytail-help card listed 5 commands, omitting /ponytail-gain,
while the Claude commands/*.toml card lists all 6. An OpenCode user wouldn't
see that ponytail-gain exists. Add it so both help surfaces agree.
2026-07-07 02:00:10 +02:00
DietrichGebert 4286903a77 fix: ship scripts/uninstall.js in the npm package (#538)
The README tells users to run 'node scripts/uninstall.js' to clean up state
left outside the plugin folder, but the package files list didn't include
scripts/, so npm-installed users didn't have the file. Add scripts/uninstall.js
to files (its only dependency, hooks/, already ships). Guard it with a test
that asserts the files entry and that the file exists.

Co-authored-by: Anmolnoor <52135717+Anmolnoor@users.noreply.github.com>
2026-07-07 01:54:04 +02:00
DietrichGebert 39bad58d40 fix: guard pi before_agent_start against bad event and missing systemPrompt (#482)
The before_agent_start handler read event.systemPrompt with no guard. A
null/undefined event crashed the agent with a TypeError (#439), and a truthy
event without a systemPrompt key stringified to the literal 'undefined',
prepending garbage to every system prompt (#440). Both are fixed with one
optional-chain guard that mirrors the event?. pattern the input handler already
uses: prepend the base prompt only when present, otherwise inject the ruleset
alone. Adds a regression test covering null/undefined event, missing
systemPrompt, and the normal base-prompt path.

Closes #439
Closes #440

Co-authored-by: Aroool <90670606+Aroool@users.noreply.github.com>
2026-07-07 01:23:31 +02:00
DietrichGebert 40e50d9e03 fix: don't crash uninstall on malformed settings.json (#481)
If settings.json contained invalid JSON, JSON.parse threw a SyntaxError, which
has no .code, so the catch rethrew it and crashed the script — after the mode
flag and config file were already removed, leaving cleanup half-done. Handle
SyntaxError explicitly: warn that the statusLine entry couldn't be removed and
leave the file untouched, since invalid JSON can't be safely edited. Adds a
regression test that a malformed settings.json exits 0, warns, and is left
byte-for-byte intact.

Closes #434

Co-authored-by: isaukywhite <50426537+isaukywhite@users.noreply.github.com>
2026-07-02 01:30:30 +02:00
DietrichGebert b8f20b8e7c docs(help): list all 6 skills and fix OpenCode command count (#480)
The ponytail-help skills table listed only 4 skills, omitting ponytail-audit
and ponytail-debt (both ship as commands). It also claimed OpenCode ships only
/ponytail and /ponytail-review, when it ships all six. Add the two missing rows
and correct the note. Regenerated the .openclaw copy so it stays in sync.

Closes #437
Closes #438
2026-07-02 01:24:32 +02:00