Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .kiro/hooks/ponytail-activate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": "v1",
"hooks": [
{
"name": "Ponytail session activation",
"trigger": "SessionStart",
"action": {
"type": "command",
"command": "PONYTAIL_HOST=kiro node hooks/ponytail-activate.js"
}
}
]
}
13 changes: 13 additions & 0 deletions .kiro/hooks/ponytail-mode-tracker.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": "v1",
"hooks": [
{
"name": "Ponytail mode tracker",
"trigger": "UserPromptSubmit",
"action": {
"type": "command",
"command": "PONYTAIL_HOST=kiro node hooks/ponytail-mode-tracker.js"
}
}
]
}
14 changes: 14 additions & 0 deletions .kiro/hooks/ponytail-subagent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"version": "v1",
"hooks": [
{
"name": "Ponytail subagent injection",
"trigger": "PreToolUse",
"matcher": "orchestrate_subagent",
"action": {
"type": "command",
"command": "PONYTAIL_HOST=kiro node hooks/ponytail-subagent.js"
}
}
]
}
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,23 @@ Qoder auto-loads `AGENTS.md` from the repo root as always-on context, so running

For full plugin-tier support (automatic mode activation + ruleset injection on every prompt), add the hooks from [`hooks/qoder-hooks.json`](hooks/qoder-hooks.json) to your `.qoder/settings.json`. Replace `PONYTAIL_DIR` with the path to your ponytail checkout. Qoder's `UserPromptSubmit` hook activates the default mode on first prompt and injects the ruleset every turn; `PreToolUse` with `task|Task` matcher injects the ruleset into subagents. Level switches (`/ponytail lite|full|ultra|off`) work automatically.

### Kiro CLI

Copy the hook files from this repo into your project or globally:

```bash
# Per-project (from a ponytail checkout):
cp -r .kiro/hooks/ /path/to/your/project/.kiro/hooks/

# Global (all Kiro sessions):
mkdir -p ~/.kiro/hooks
cp .kiro/hooks/ponytail-*.json ~/.kiro/hooks/
```

The hooks reference the `hooks/` scripts relative to the project root, so either clone ponytail into your project or update the paths in the JSON files to point at your checkout (see [`hooks/kiro-hooks.json`](hooks/kiro-hooks.json) for a template with `PONYTAIL_DIR` placeholders).

This gives full hook support: session activation, mode switching (`/ponytail lite|full|ultra|off`), and subagent injection via `PreToolUse`. The steering file (`.kiro/steering/ponytail.md`) remains as an instruction-only fallback — copy it to `~/.kiro/steering/` for always-on rules without hooks.

### Antigravity CLI

Google is renaming Gemini CLI to Antigravity CLI (the `agy` binary); the same extension installs there:
Expand Down Expand Up @@ -273,7 +290,7 @@ While active, the ruleset is also injected into every subagent spawned via the A

Cursor, Windsurf, Cline, GitHub Copilot Chat (the VS Code, JetBrains, and Visual Studio editor extension, not the standalone Copilot CLI covered under [Install](#install)), Aider, Kiro, Zed, CodeWhale, Swival, Qoder: copy the matching rules file from this repo ([`.cursor/rules/`](.cursor/rules/), [`.windsurf/rules/`](.windsurf/rules/), [`.clinerules/`](.clinerules/), [`.github/copilot-instructions.md`](.github/copilot-instructions.md), [`AGENTS.md`](AGENTS.md), [`.kiro/steering/`](.kiro/steering/), [`.qoder/rules/`](.qoder/rules/)).

Kiro: copy `.kiro/steering/ponytail.md` to `~/.kiro/steering/` (global) or `.kiro/steering/` in your project.
Kiro: copy `.kiro/steering/ponytail.md` to `~/.kiro/steering/` (global) or `.kiro/steering/` in your project. For full hook support (mode switching, subagent injection), see [Kiro CLI](#kiro-cli) under Install.

GitHub Copilot CLI fallback (instruction-only mode): it reads `AGENTS.md` and `.github/copilot-instructions.md` in a project, or copy the rules into `~/.copilot/copilot-instructions.md` to run ponytail in every project. This path keeps always-on guidance, but does not add plugin mode switches or hooks.

Expand All @@ -296,6 +313,7 @@ Which files map to which agent: [Agent portability](docs/agent-portability.md).
| Devin CLI | `devin plugins remove ponytail` |
| Grok Build | `grok plugin uninstall ponytail` |
| Pi agent | `pi uninstall ponytail` |
| Kiro | Delete `.kiro/hooks/ponytail-*.json` and `.kiro/steering/ponytail.md` |
| Cursor / Windsurf / Cline / Qoder / etc. | Delete the copied rule file |

These remove the plugin's own files. They leave behind a small amount of state ponytail writes outside the plugin folder: the mode flag, `~/.config/ponytail/config.json`, and (if you accepted the setup nudge) a `statusLine` entry in `~/.claude/settings.json`. Run `node scripts/uninstall.js` to clean those up too. **Run it before the host remove command above** — the script is itself a plugin file, so removing the plugin first deletes it (or run it from a separate clone of this repo). It only removes the statusLine entry if it points at ponytail's own script, so a statusline you set up yourself is left untouched.
Expand All @@ -311,7 +329,7 @@ These remove the plugin's own files. They leave behind a small amount of state p
| `/ponytail-gain` | Show the measured impact scoreboard (less code, less cost, more speed) from the benchmark. |
| `/ponytail-help` | Quick reference for the commands above. |

Commands need a skill-capable host (Claude Code, Codex, Devin CLI, OpenCode, Gemini, pi, Swival, Hermes Agent, Qoder, Grok Build). In Codex they're skills, invoke with `@` (`@ponytail-review`). The instruction-only adapters (Cursor, Windsurf, Cline, Copilot, Kiro, Antigravity) load the always-on ruleset without the commands.
Commands need a skill-capable host (Claude Code, Codex, Devin CLI, OpenCode, Gemini, pi, Swival, Hermes Agent, Qoder, Grok Build). In Codex they're skills, invoke with `@` (`@ponytail-review`). The instruction-only adapters (Cursor, Windsurf, Cline, Copilot, Antigravity) load the always-on ruleset without the commands. Kiro with hooks supports mode switching (`/ponytail lite|full|ultra|off`) but not the slash-command skills.

## Development

Expand Down
2 changes: 1 addition & 1 deletion docs/agent-portability.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ to load in a given agent.
| JetBrains Junie | `AGENTS.md` | Junie reads `AGENTS.md` once you point it there in Settings → Tools → Junie → Project Settings → Guidelines Path (not automatic yet); this repo ships `AGENTS.md`, and `.junie/guidelines.md` is Junie's legacy path. Instruction-tier. |
| Amp (Sourcegraph) | `AGENTS.md` | Amp reads `AGENTS.md` from the working directory and parent directories up to `$HOME` (plus global config like `~/.config/amp/AGENTS.md`); falls back to `AGENT.md`/`CLAUDE.md`. Instruction-tier. |
| Jules (Google) | `AGENTS.md` | Jules automatically reads `AGENTS.md` from the repository root. Instruction-tier. |
| Kiro | `.kiro/steering/ponytail.md` | Steering rule; copy globally or into a project. |
| Kiro | `.kiro/steering/ponytail.md`, `.kiro/hooks/ponytail-*.json`, `hooks/kiro-hooks.json`, `hooks/` | Instruction-only (steering file) or full hook support (v2 hooks for session activation, mode tracking, and subagent injection). Copy `.kiro/hooks/` into your project or `~/.kiro/hooks/` for global activation. Hooks wire to the shared `hooks/` scripts via `PONYTAIL_HOST=kiro`. |
| Qoder | `.qoder/rules/ponytail.md`, `.qoder-plugin/plugin.json`, `hooks/qoder-hooks.json`, `skills/`, `AGENTS.md` | Qoder auto-loads `AGENTS.md` as always-on context; `.qoder/rules/ponytail.md` provides per-project rules; the plugin manifest points at `skills/` for the six ponytail skills (invoked as `/ponytail`, `/ponytail-review`, etc. via the Skill system). Full plugin-tier: `hooks/qoder-hooks.json` template registers `UserPromptSubmit` (mode activation + ruleset injection) and `PreToolUse` with `task|Task` matcher (subagent injection). Instruction-tier works from repo root with zero setup via `AGENTS.md`. |
| Zed | `AGENTS.md` | Auto-includes `AGENTS.md` from the worktree root as one of its default rule files for the Agent Panel. Instruction-tier. |
| Generic agents | `AGENTS.md` or `skills/*/SKILL.md` | Copy the compact rule file or load the skill files directly. |
Expand Down
31 changes: 31 additions & 0 deletions hooks/kiro-hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"_comment": "Reference template — copy the files from .kiro/hooks/ into your project's .kiro/hooks/ or ~/.kiro/hooks/ for global activation. Replace PONYTAIL_DIR with the path to your ponytail checkout if hooks are not relative to the project root.",
"version": "v1",
"hooks": [
{
"name": "Ponytail session activation",
"trigger": "SessionStart",
"action": {
"type": "command",
"command": "PONYTAIL_HOST=kiro node PONYTAIL_DIR/hooks/ponytail-activate.js"
}
},
{
"name": "Ponytail mode tracker",
"trigger": "UserPromptSubmit",
"action": {
"type": "command",
"command": "PONYTAIL_HOST=kiro node PONYTAIL_DIR/hooks/ponytail-mode-tracker.js"
}
},
{
"name": "Ponytail subagent injection",
"trigger": "PreToolUse",
"matcher": "orchestrate_subagent",
"action": {
"type": "command",
"command": "PONYTAIL_HOST=kiro node PONYTAIL_DIR/hooks/ponytail-subagent.js"
}
}
]
}
5 changes: 3 additions & 2 deletions hooks/ponytail-activate.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const {
clearMode,
isCodex,
isCopilot,
isKiro,
setMode,
writeHookOutput,
} = require('./ponytail-runtime');
Expand All @@ -26,7 +27,7 @@ const mode = getDefaultMode();
// "off" mode — skip activation entirely, don't write flag or emit rules
if (mode === 'off') {
clearMode();
const hookOutput = (isCodex || isCopilot) ? '' : 'OK';
const hookOutput = (isCodex || isCopilot || isKiro) ? '' : 'OK';
writeHookOutput('SessionStart', 'off', hookOutput);
process.exit(0);
}
Expand All @@ -42,7 +43,7 @@ try {
let output = getPonytailInstructions(mode);

// 3. Detect missing statusline config — nudge Claude to help set it up
if (!isCodex && !isCopilot) try {
if (!isCodex && !isCopilot && !isKiro) try {
let hasStatusline = false;
if (fs.existsSync(settingsPath)) {
// Strip UTF-8 BOM some editors prepend on Windows (breaks JSON.parse)
Expand Down
11 changes: 11 additions & 0 deletions hooks/ponytail-runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,17 @@ const isCopilot = Boolean(process.env.COPILOT_PLUGIN_DATA) ||
isVsCodeCopilotRoot(process.env.CLAUDE_PLUGIN_ROOT);
const isCodex = !isCopilot && Boolean(process.env.PLUGIN_DATA);
const isQoder = !isCopilot && !isCodex && Boolean(process.env.QODER_SESSION_ID);
const isKiro = !isCopilot && !isCodex && !isQoder &&
(process.env.PONYTAIL_HOST || '').toLowerCase() === 'kiro';
Comment on lines 21 to +24

let stateDir = getClaudeDir();
if (isCodex) stateDir = process.env.PLUGIN_DATA;
// COPILOT_PLUGIN_DATA is unset under VS Code Copilot, so fall back to
// getClaudeDir() rather than building a path from undefined.
if (isCopilot) stateDir = process.env.COPILOT_PLUGIN_DATA || getClaudeDir();
if (isQoder) stateDir = path.join(os.homedir(), '.qoder');
// Kiro: state lives under KIRO_HOME (documented profile redirect) or ~/.kiro.
if (isKiro) stateDir = process.env.KIRO_HOME || path.join(os.homedir(), '.kiro');

const statePath = path.join(stateDir, STATE_FILE);

Expand Down Expand Up @@ -79,6 +83,12 @@ function writeHookOutput(event, mode, context = '') {
process.stdout.write(JSON.stringify(output));
return;
}
// Kiro: raw stdout is forwarded as context for SessionStart and
// UserPromptSubmit hooks (exit 0). Same semantics as native Claude Code.
if (isKiro) {
process.stdout.write(context);
return;
Comment on lines +86 to +90
}
// Native Claude: SessionStart accepts raw stdout, but SubagentStart needs the
// hookSpecificOutput JSON form or the context is dropped.
if (event === 'SubagentStart') {
Expand All @@ -93,6 +103,7 @@ module.exports = {
clearMode,
isCodex,
isCopilot,
isKiro,
isQoder,
readMode,
setMode,
Expand Down