From 2d086d3084b2d5232b94eb81a96bdb0b771b5b1a Mon Sep 17 00:00:00 2001 From: "Sparsh :)" <76697238+SparshGarg999@users.noreply.github.com> Date: Sat, 27 Jun 2026 22:12:17 +0530 Subject: [PATCH] feat: add Factory Droid adapter, mode shortcuts, and custom skills (jedi, plan, ask) --- .factory-plugin/plugin.json | 10 +++++ .openclaw/skills/ponytail-ask/SKILL.md | 8 ++++ .openclaw/skills/ponytail-help/SKILL.md | 9 ++-- .openclaw/skills/ponytail-jedi/SKILL.md | 8 ++++ .openclaw/skills/ponytail-plan/SKILL.md | 8 ++++ .opencode/command/ponytail-ask.md | 5 +++ .opencode/command/ponytail-jedi.md | 5 +++ .opencode/command/ponytail-lightsaber.md | 5 +++ .opencode/command/ponytail-plan.md | 5 +++ commands/ponytail-ask.toml | 2 + commands/ponytail-jedi.toml | 2 + commands/ponytail-lightsaber.toml | 2 + commands/ponytail-plan.toml | 2 + docs/agent-portability.md | 1 + hooks/factory-hooks.json | 44 ++++++++++++++++++ hooks/ponytail-activate.js | 3 +- hooks/ponytail-runtime.js | 6 ++- pi-extension/index.js | 57 ++++++++++++++++++++++++ pi-extension/test/extension.test.js | 37 ++++++++++++++- plugin.yaml | 7 +++ scripts/build-openclaw-skills.js | 3 ++ skills/ponytail-ask/SKILL.md | 9 ++++ skills/ponytail-help/SKILL.md | 9 ++-- skills/ponytail-jedi/SKILL.md | 10 +++++ skills/ponytail-plan/SKILL.md | 9 ++++ tests/factory-plugin.test.js | 42 +++++++++++++++++ tests/hooks.test.js | 33 ++++++++++++++ 27 files changed, 331 insertions(+), 10 deletions(-) create mode 100644 .factory-plugin/plugin.json create mode 100644 .openclaw/skills/ponytail-ask/SKILL.md create mode 100644 .openclaw/skills/ponytail-jedi/SKILL.md create mode 100644 .openclaw/skills/ponytail-plan/SKILL.md create mode 100644 .opencode/command/ponytail-ask.md create mode 100644 .opencode/command/ponytail-jedi.md create mode 100644 .opencode/command/ponytail-lightsaber.md create mode 100644 .opencode/command/ponytail-plan.md create mode 100644 commands/ponytail-ask.toml create mode 100644 commands/ponytail-jedi.toml create mode 100644 commands/ponytail-lightsaber.toml create mode 100644 commands/ponytail-plan.toml create mode 100644 hooks/factory-hooks.json create mode 100644 skills/ponytail-ask/SKILL.md create mode 100644 skills/ponytail-jedi/SKILL.md create mode 100644 skills/ponytail-plan/SKILL.md create mode 100644 tests/factory-plugin.test.js diff --git a/.factory-plugin/plugin.json b/.factory-plugin/plugin.json new file mode 100644 index 00000000..646eeda1 --- /dev/null +++ b/.factory-plugin/plugin.json @@ -0,0 +1,10 @@ +{ + "name": "ponytail", + "version": "4.8.3", + "description": "Lazy senior dev mode. Forces the simplest, shortest solution that actually works: YAGNI, stdlib first, no unrequested abstractions.", + "author": { + "name": "Dietrich Gebert", + "url": "https://github.com/DietrichGebert" + }, + "hooks": "./hooks/factory-hooks.json" +} diff --git a/.openclaw/skills/ponytail-ask/SKILL.md b/.openclaw/skills/ponytail-ask/SKILL.md new file mode 100644 index 00000000..81ed7128 --- /dev/null +++ b/.openclaw/skills/ponytail-ask/SKILL.md @@ -0,0 +1,8 @@ +--- +name: ponytail-ask +description: "Clarify requirements and ask targeted questions to avoid spec building." +homepage: https://github.com/DietrichGebert/ponytail +license: MIT +--- + +Understand the user's requirements, clarify ambiguity, and ask targeted questions to refine the task before coding. Keep questions focused and concise, ensuring we only build what is actually requested and avoid speculative features. diff --git a/.openclaw/skills/ponytail-help/SKILL.md b/.openclaw/skills/ponytail-help/SKILL.md index 1d59c9ef..bb7cc159 100644 --- a/.openclaw/skills/ponytail-help/SKILL.md +++ b/.openclaw/skills/ponytail-help/SKILL.md @@ -26,12 +26,15 @@ Level sticks until changed or session end. |-------|---------|--------------| | **ponytail** | `/ponytail` | Lazy mode itself. Simplest solution that works. | | **ponytail-review** | `/ponytail-review` | Over-engineering review: `L42: yagni: factory, one product. Inline.` | +| **ponytail-audit** | `/ponytail-audit` | Whole-repo audit for over-engineering, ranked list of what to delete. | +| **ponytail-debt** | `/ponytail-debt` | Harvest every ponytail: shortcut comment into one debt ledger. | | **ponytail-gain** | `/ponytail-gain` | Measured-impact scoreboard: less code, less cost, more speed. | +| **ponytail-jedi** | `/ponytail-jedi` | SOTA research: search, rank, clone, modify, reuse trusted repos. | +| **ponytail-plan** | `/ponytail-plan` | Create or simplify implementation plans to be clean, simple, and minimal. | +| **ponytail-ask** | `/ponytail-ask` | Clarify requirements and ask targeted questions to avoid spec building. | | **ponytail-help** | `/ponytail-help` | This card. | -Codex uses `@ponytail`, `@ponytail-review`, and `@ponytail-help`; Claude Code -and OpenCode use the slash-command forms above (OpenCode ships `/ponytail` and -`/ponytail-review`). +Codex uses `@ponytail`, `@ponytail-review`, etc.; Claude Code, OpenCode, and Droid use the slash-command forms. ## Deactivate diff --git a/.openclaw/skills/ponytail-jedi/SKILL.md b/.openclaw/skills/ponytail-jedi/SKILL.md new file mode 100644 index 00000000..e50f2ae4 --- /dev/null +++ b/.openclaw/skills/ponytail-jedi/SKILL.md @@ -0,0 +1,8 @@ +--- +name: ponytail-jedi +description: "SOTA research: search, rank, clone, modify, reuse trusted repos." +homepage: https://github.com/DietrichGebert/ponytail +license: MIT +--- + +Perform deep open-source SOTA (State of the Art) research for the given task. Do not start from scratch: search, rank, clone, modify, extract, or connect to world-class trusted repositories that can do part or all of the work. Find and reuse existing high-quality, trusted open-source code rather than reinventing the wheel. diff --git a/.openclaw/skills/ponytail-plan/SKILL.md b/.openclaw/skills/ponytail-plan/SKILL.md new file mode 100644 index 00000000..9b7b5c33 --- /dev/null +++ b/.openclaw/skills/ponytail-plan/SKILL.md @@ -0,0 +1,8 @@ +--- +name: ponytail-plan +description: "Create or simplify implementation plans to be clean, simple, and minimal." +homepage: https://github.com/DietrichGebert/ponytail +license: MIT +--- + +Understand the user's task and create a clean, simple, and minimal implementation plan. Break down the task into component-level items and simplify the approach to avoid unnecessary abstractions, layers, or dependencies. If the user already has a plan, audit and simplify it to be clean and minimal. diff --git a/.opencode/command/ponytail-ask.md b/.opencode/command/ponytail-ask.md new file mode 100644 index 00000000..dd4714b6 --- /dev/null +++ b/.opencode/command/ponytail-ask.md @@ -0,0 +1,5 @@ +--- +description: Clarify requirements and ask targeted questions to refine tasks before coding +--- + +Understand the user's requirements, clarify ambiguity, and ask targeted questions to refine the task before coding. Keep questions focused and concise, ensuring we only build what is actually requested and avoid speculative features. diff --git a/.opencode/command/ponytail-jedi.md b/.opencode/command/ponytail-jedi.md new file mode 100644 index 00000000..6fd79e3c --- /dev/null +++ b/.opencode/command/ponytail-jedi.md @@ -0,0 +1,5 @@ +--- +description: Research SOTA open source solutions instead of starting from scratch +--- + +Perform deep open-source SOTA (State of the Art) research for the given task. Do not start from scratch: search, rank, clone, modify, extract, or connect to world-class trusted repositories that can do part or all of the work. Find and reuse existing high-quality, trusted open-source code rather than reinventing the wheel. diff --git a/.opencode/command/ponytail-lightsaber.md b/.opencode/command/ponytail-lightsaber.md new file mode 100644 index 00000000..6fd79e3c --- /dev/null +++ b/.opencode/command/ponytail-lightsaber.md @@ -0,0 +1,5 @@ +--- +description: Research SOTA open source solutions instead of starting from scratch +--- + +Perform deep open-source SOTA (State of the Art) research for the given task. Do not start from scratch: search, rank, clone, modify, extract, or connect to world-class trusted repositories that can do part or all of the work. Find and reuse existing high-quality, trusted open-source code rather than reinventing the wheel. diff --git a/.opencode/command/ponytail-plan.md b/.opencode/command/ponytail-plan.md new file mode 100644 index 00000000..88bf2cb4 --- /dev/null +++ b/.opencode/command/ponytail-plan.md @@ -0,0 +1,5 @@ +--- +description: Create or simplify implementation plans to be clean, simple, and minimal +--- + +Understand the user's task and create a clean, simple, and minimal implementation plan. Break down the task into component-level items and simplify the approach to avoid unnecessary abstractions, layers, or dependencies. If the user already has a plan, audit and simplify it to be clean and minimal. diff --git a/commands/ponytail-ask.toml b/commands/ponytail-ask.toml new file mode 100644 index 00000000..7ec8be16 --- /dev/null +++ b/commands/ponytail-ask.toml @@ -0,0 +1,2 @@ +description = "Clarify requirements and ask targeted questions to refine tasks before coding" +prompt = "Understand the user's requirements, clarify ambiguity, and ask targeted questions to refine the task before coding. Keep questions focused and concise, ensuring we only build what is actually requested and avoid speculative features." diff --git a/commands/ponytail-jedi.toml b/commands/ponytail-jedi.toml new file mode 100644 index 00000000..ff5e90f3 --- /dev/null +++ b/commands/ponytail-jedi.toml @@ -0,0 +1,2 @@ +description = "Research SOTA open source solutions instead of starting from scratch" +prompt = "Perform deep open-source SOTA (State of the Art) research for the given task. Do not start from scratch: search, rank, clone, modify, extract, or connect to world-class trusted repositories that can do part or all of the work. Find and reuse existing high-quality, trusted open-source code rather than reinventing the wheel." diff --git a/commands/ponytail-lightsaber.toml b/commands/ponytail-lightsaber.toml new file mode 100644 index 00000000..ff5e90f3 --- /dev/null +++ b/commands/ponytail-lightsaber.toml @@ -0,0 +1,2 @@ +description = "Research SOTA open source solutions instead of starting from scratch" +prompt = "Perform deep open-source SOTA (State of the Art) research for the given task. Do not start from scratch: search, rank, clone, modify, extract, or connect to world-class trusted repositories that can do part or all of the work. Find and reuse existing high-quality, trusted open-source code rather than reinventing the wheel." diff --git a/commands/ponytail-plan.toml b/commands/ponytail-plan.toml new file mode 100644 index 00000000..786eb03f --- /dev/null +++ b/commands/ponytail-plan.toml @@ -0,0 +1,2 @@ +description = "Create or simplify implementation plans to be clean, simple, and minimal" +prompt = "Understand the user's task and create a clean, simple, and minimal implementation plan. Break down the task into component-level items and simplify the approach to avoid unnecessary abstractions, layers, or dependencies. If the user already has a plan, audit and simplify it to be clean and minimal." diff --git a/docs/agent-portability.md b/docs/agent-portability.md index bdae7111..a845749f 100644 --- a/docs/agent-portability.md +++ b/docs/agent-portability.md @@ -19,6 +19,7 @@ to load in a given agent. | Cline | `.clinerules/ponytail.md` | Project rule. | | GitHub Copilot | `.github/copilot-instructions.md` | Repository instruction file. | | GitHub Copilot CLI | `.github/plugin/`, `AGENTS.md`, `.github/copilot-instructions.md`, `~/.copilot/copilot-instructions.md` | Plugin-supported (`copilot plugin marketplace add DietrichGebert/ponytail` + `copilot plugin install ponytail@ponytail`). Fallback instruction mode remains: per-project from `AGENTS.md` or `.github/copilot-instructions.md`, or globally from `~/.copilot/copilot-instructions.md` (instruction-tier, no `/ponytail` levels or hooks). | +| Factory Droid | `.factory-plugin/plugin.json`, `hooks/factory-hooks.json`, `skills/`, `hooks/`, `AGENTS.md` | Plugin install with session activation, mode tracking, and commands. Fallback instruction mode: copy `AGENTS.md` + `skills/` to `~/.factory/` or repo root. | | Antigravity | `AGENTS.md` | Reads `AGENTS.md` at the repo root as always-on rules (like `.cursorrules`/`CLAUDE.md`); `.agents/rules/` also works for workspace rules. Instruction-tier. | | CodeWhale | `AGENTS.md` | Reads `AGENTS.md` from the repo root as project instructions; also reads `CLAUDE.md` and `.claude/instructions.md` as fallbacks. Instruction-tier. | | Swival | `.swival/skills/`, `AGENTS.md` | `swival skills add https://github.com/DietrichGebert/ponytail` installs the six skills straight into `.swival/skills/`. Add `--global` to stage them in the library (`~/.config/swival/library`) first, then `swival skills add ponytail` (or `--global ponytail`) to activate per-project or everywhere. Also reads `AGENTS.md` from the repo root and `~/.config/swival/AGENTS.md` globally as instruction-tier fallback. | diff --git a/hooks/factory-hooks.json b/hooks/factory-hooks.json new file mode 100644 index 00000000..a5380a3f --- /dev/null +++ b/hooks/factory-hooks.json @@ -0,0 +1,44 @@ +{ + "hooks": { + "SessionStart": [ + { + "matcher": "startup|resume|clear|compact", + "hooks": [ + { + "type": "command", + "command": "node \"${DROID_PLUGIN_ROOT}/hooks/ponytail-activate.js\"; exit 0", + "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { node \"$env:DROID_PLUGIN_ROOT\\hooks\\ponytail-activate.js\" }", + "timeout": 5, + "statusMessage": "Loading ponytail mode..." + } + ] + } + ], + "SubagentStart": [ + { + "hooks": [ + { + "type": "command", + "command": "node \"${DROID_PLUGIN_ROOT}/hooks/ponytail-subagent.js\"; exit 0", + "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { node \"$env:DROID_PLUGIN_ROOT\\hooks\\ponytail-subagent.js\" }", + "timeout": 5, + "statusMessage": "Loading ponytail mode..." + } + ] + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "type": "command", + "command": "node \"${DROID_PLUGIN_ROOT}/hooks/ponytail-mode-tracker.js\"; exit 0", + "commandWindows": "if (Get-Command node -ErrorAction SilentlyContinue) { node \"$env:DROID_PLUGIN_ROOT\\hooks\\ponytail-mode-tracker.js\" }", + "timeout": 5, + "statusMessage": "Tracking ponytail mode..." + } + ] + } + ] + } +} diff --git a/hooks/ponytail-activate.js b/hooks/ponytail-activate.js index 25f4e48e..4d58a8f1 100644 --- a/hooks/ponytail-activate.js +++ b/hooks/ponytail-activate.js @@ -14,6 +14,7 @@ const { clearMode, isCodex, isCopilot, + isDroid, setMode, writeHookOutput, } = require('./ponytail-runtime'); @@ -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 && !isDroid) try { let hasStatusline = false; if (fs.existsSync(settingsPath)) { // Strip UTF-8 BOM some editors prepend on Windows (breaks JSON.parse) diff --git a/hooks/ponytail-runtime.js b/hooks/ponytail-runtime.js index 2aa61a31..c0c06920 100644 --- a/hooks/ponytail-runtime.js +++ b/hooks/ponytail-runtime.js @@ -1,14 +1,17 @@ const fs = require('fs'); const path = require('path'); +const os = require('os'); const { getClaudeDir } = require('./ponytail-config'); const STATE_FILE = '.ponytail-active'; const isCopilot = Boolean(process.env.COPILOT_PLUGIN_DATA); const isCodex = !isCopilot && Boolean(process.env.PLUGIN_DATA); +const isDroid = !isCopilot && !isCodex && Boolean(process.env.DROID_PLUGIN_ROOT); let stateDir = getClaudeDir(); if (isCodex) stateDir = process.env.PLUGIN_DATA; if (isCopilot) stateDir = process.env.COPILOT_PLUGIN_DATA; +if (isDroid) stateDir = process.env.DROID_CONFIG_DIR || path.join(os.homedir(), '.factory'); const statePath = path.join(stateDir, STATE_FILE); @@ -37,7 +40,7 @@ function writeHookOutput(event, mode, context = '') { event === 'SessionStart' && context ? { additionalContext: context } : {})); return; } - if (isCodex) { + if (isCodex || isDroid) { const output = { systemMessage: `PONYTAIL:${mode.toUpperCase()}` }; if (context) { output.hookSpecificOutput = { @@ -62,6 +65,7 @@ module.exports = { clearMode, isCodex, isCopilot, + isDroid, readMode, setMode, writeHookOutput, diff --git a/pi-extension/index.js b/pi-extension/index.js index 5f176270..b714e6d0 100644 --- a/pi-extension/index.js +++ b/pi-extension/index.js @@ -155,6 +155,63 @@ export default function ponytailExtension(pi) { handler: (_args, ctx) => sendAlias("/skill:ponytail-help", "", ctx), }); + pi.registerCommand("ponytail-jedi", { + description: "Run /skill:ponytail-jedi", + handler: (_args, ctx) => sendAlias("/skill:ponytail-jedi", "", ctx), + }); + + pi.registerCommand("ponytail-lightsaber", { + description: "Run /skill:ponytail-jedi (research)", + handler: (_args, ctx) => sendAlias("/skill:ponytail-jedi", "", ctx), + }); + + pi.registerCommand("ponytail-plan", { + description: "Run /skill:ponytail-plan", + handler: (_args, ctx) => sendAlias("/skill:ponytail-plan", "", ctx), + }); + + pi.registerCommand("ponytail-ask", { + description: "Run /skill:ponytail-ask", + handler: (_args, ctx) => sendAlias("/skill:ponytail-ask", "", ctx), + }); + + pi.registerShortcut("ponytail:cycleMode", { + description: "Cycle ponytail modes (off -> lite -> full -> ultra -> off)", + handler: async (ctx) => { + const nextModes = { off: "lite", lite: "full", full: "ultra", ultra: "off" }; + const nextMode = nextModes[currentMode] || "full"; + setMode(nextMode, ctx); + }, + }); + + pi.registerShortcut("ponytail:modeLite", { + description: "Set ponytail mode to lite", + handler: async (ctx) => { + setMode("lite", ctx); + }, + }); + + pi.registerShortcut("ponytail:modeFull", { + description: "Set ponytail mode to full", + handler: async (ctx) => { + setMode("full", ctx); + }, + }); + + pi.registerShortcut("ponytail:modeUltra", { + description: "Set ponytail mode to ultra", + handler: async (ctx) => { + setMode("ultra", ctx); + }, + }); + + pi.registerShortcut("ponytail:modeOff", { + description: "Turn ponytail mode off", + handler: async (ctx) => { + setMode("off", ctx); + }, + }); + pi.on("input", async (event) => { if (event?.source === "extension") return; diff --git a/pi-extension/test/extension.test.js b/pi-extension/test/extension.test.js index b5f3918d..f3324934 100644 --- a/pi-extension/test/extension.test.js +++ b/pi-extension/test/extension.test.js @@ -9,6 +9,7 @@ import ponytailExtension from "../index.js"; function createPiHarness() { const events = new Map(); const commands = new Map(); + const shortcuts = new Map(); const appendedEntries = []; const sentUserMessages = []; @@ -19,6 +20,9 @@ function createPiHarness() { registerCommand(name, options) { commands.set(name, options); }, + registerShortcut(keyId, options) { + shortcuts.set(keyId, options); + }, appendEntry(customType, data) { appendedEntries.push({ customType, data }); }, @@ -28,7 +32,7 @@ function createPiHarness() { }; ponytailExtension(pi); - return { events, commands, appendedEntries, sentUserMessages }; + return { events, commands, shortcuts, appendedEntries, sentUserMessages }; } function createCommandContext(overrides = {}) { @@ -57,7 +61,36 @@ function withTempConfig(fn) { test("extension registers Ponytail commands", () => { const { commands } = createPiHarness(); - assert.deepEqual([...commands.keys()].sort(), ["ponytail", "ponytail-audit", "ponytail-debt", "ponytail-gain", "ponytail-help", "ponytail-review"]); + assert.deepEqual( + [...commands.keys()].sort(), + [ + "ponytail", + "ponytail-ask", + "ponytail-audit", + "ponytail-debt", + "ponytail-gain", + "ponytail-help", + "ponytail-jedi", + "ponytail-lightsaber", + "ponytail-plan", + "ponytail-review", + ] + ); +}); + +test("extension registers Ponytail shortcuts", () => { + const { shortcuts } = createPiHarness(); + + assert.deepEqual( + [...shortcuts.keys()].sort(), + [ + "ponytail:cycleMode", + "ponytail:modeFull", + "ponytail:modeLite", + "ponytail:modeOff", + "ponytail:modeUltra", + ] + ); }); test("/ponytail updates session mode and injects instructions", async () => withTempConfig(async () => { diff --git a/plugin.yaml b/plugin.yaml index 3b911c0f..caa21946 100644 --- a/plugin.yaml +++ b/plugin.yaml @@ -12,6 +12,10 @@ provides_commands: - ponytail-debt - ponytail-gain - ponytail-help + - ponytail-jedi + - ponytail-lightsaber + - ponytail-plan + - ponytail-ask provides_skills: - ponytail - ponytail-review @@ -19,3 +23,6 @@ provides_skills: - ponytail-debt - ponytail-gain - ponytail-help + - ponytail-jedi + - ponytail-plan + - ponytail-ask diff --git a/scripts/build-openclaw-skills.js b/scripts/build-openclaw-skills.js index 9fe215c9..e29ad90a 100644 --- a/scripts/build-openclaw-skills.js +++ b/scripts/build-openclaw-skills.js @@ -23,6 +23,9 @@ const DESCRIPTIONS = { 'ponytail-debt': 'Harvest every ponytail: shortcut comment into one debt ledger, so deferrals get tracked instead of forgotten. One-shot report.', 'ponytail-gain': 'Show ponytail measured impact as a scoreboard: less code, less cost, more speed, from the benchmark medians. One-shot display.', 'ponytail-help': "Quick reference for ponytail's modes, skills, and commands. One-shot display.", + 'ponytail-jedi': 'SOTA research: search, rank, clone, modify, reuse trusted repos.', + 'ponytail-plan': 'Create or simplify implementation plans to be clean, simple, and minimal.', + 'ponytail-ask': 'Clarify requirements and ask targeted questions to avoid spec building.', }; const NAMES = Object.keys(DESCRIPTIONS); diff --git a/skills/ponytail-ask/SKILL.md b/skills/ponytail-ask/SKILL.md new file mode 100644 index 00000000..5a3722cb --- /dev/null +++ b/skills/ponytail-ask/SKILL.md @@ -0,0 +1,9 @@ +--- +name: ponytail-ask +description: > + Clarify requirements and ask targeted questions to refine tasks before writing code. + Ensures we only build what is requested. Use when the user says "ask questions", + "clarify requirements", "what is needed", or invokes /ponytail-ask. +--- + +Understand the user's requirements, clarify ambiguity, and ask targeted questions to refine the task before coding. Keep questions focused and concise, ensuring we only build what is actually requested and avoid speculative features. diff --git a/skills/ponytail-help/SKILL.md b/skills/ponytail-help/SKILL.md index 7e096bf2..8fe7db5d 100644 --- a/skills/ponytail-help/SKILL.md +++ b/skills/ponytail-help/SKILL.md @@ -27,12 +27,15 @@ Level sticks until changed or session end. |-------|---------|--------------| | **ponytail** | `/ponytail` | Lazy mode itself. Simplest solution that works. | | **ponytail-review** | `/ponytail-review` | Over-engineering review: `L42: yagni: factory, one product. Inline.` | +| **ponytail-audit** | `/ponytail-audit` | Whole-repo audit for over-engineering, ranked list of what to delete. | +| **ponytail-debt** | `/ponytail-debt` | Harvest every ponytail: shortcut comment into one debt ledger. | | **ponytail-gain** | `/ponytail-gain` | Measured-impact scoreboard: less code, less cost, more speed. | +| **ponytail-jedi** | `/ponytail-jedi` | SOTA research: search, rank, clone, modify, reuse trusted repos. | +| **ponytail-plan** | `/ponytail-plan` | Create or simplify implementation plans to be clean, simple, and minimal. | +| **ponytail-ask** | `/ponytail-ask` | Clarify requirements and ask targeted questions to avoid spec building. | | **ponytail-help** | `/ponytail-help` | This card. | -Codex uses `@ponytail`, `@ponytail-review`, and `@ponytail-help`; Claude Code -and OpenCode use the slash-command forms above (OpenCode ships `/ponytail` and -`/ponytail-review`). +Codex uses `@ponytail`, `@ponytail-review`, etc.; Claude Code, OpenCode, and Droid use the slash-command forms. ## Deactivate diff --git a/skills/ponytail-jedi/SKILL.md b/skills/ponytail-jedi/SKILL.md new file mode 100644 index 00000000..b28961fe --- /dev/null +++ b/skills/ponytail-jedi/SKILL.md @@ -0,0 +1,10 @@ +--- +name: ponytail-jedi +description: > + Search, rank, clone, modify, extract, or connect to world-class trusted open-source + repositories. SOTA research to avoid starting from scratch. Use when the user says + "research SOTA", "use open-source repos", "find a trusted repo", or invokes + /ponytail-jedi / /ponytail-lightsaber. +--- + +Perform deep open-source SOTA (State of the Art) research for the given task. Do not start from scratch: search, rank, clone, modify, extract, or connect to world-class trusted repositories that can do part or all of the work. Find and reuse existing high-quality, trusted open-source code rather than reinventing the wheel. diff --git a/skills/ponytail-plan/SKILL.md b/skills/ponytail-plan/SKILL.md new file mode 100644 index 00000000..5d8cc68c --- /dev/null +++ b/skills/ponytail-plan/SKILL.md @@ -0,0 +1,9 @@ +--- +name: ponytail-plan +description: > + Create or simplify implementation plans to be clean, simple, and minimal. + YAGNI-first planning. Use when the user says "create a plan", "simplify my plan", + "audit plan", "plan this task", or invokes /ponytail-plan. +--- + +Understand the user's task and create a clean, simple, and minimal implementation plan. Break down the task into component-level items and simplify the approach to avoid unnecessary abstractions, layers, or dependencies. If the user already has a plan, audit and simplify it to be clean and minimal. diff --git a/tests/factory-plugin.test.js b/tests/factory-plugin.test.js new file mode 100644 index 00000000..4d941f14 --- /dev/null +++ b/tests/factory-plugin.test.js @@ -0,0 +1,42 @@ +#!/usr/bin/env node +// Smoke test for the Factory Droid plugin adapter: keep command wiring minimal and +// ensure the hooks path in manifest points to the correct hooks.json. + +const test = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('fs'); +const path = require('path'); + +const root = path.join(__dirname, '..'); +const REQUIRED_COMMAND_FILES = [ + 'ponytail.toml', + 'ponytail-review.toml', + 'ponytail-audit.toml', + 'ponytail-debt.toml', + 'ponytail-jedi.toml', + 'ponytail-lightsaber.toml', + 'ponytail-plan.toml', + 'ponytail-ask.toml', +]; + +function readJSON(relPath) { + return JSON.parse(fs.readFileSync(path.join(root, relPath), 'utf8')); +} + +test('factory plugin hooks and commands check', () => { + const manifest = readJSON('.factory-plugin/plugin.json'); + assert.equal(manifest.name, 'ponytail'); + assert.equal(manifest.hooks, './hooks/factory-hooks.json'); + + assert.ok( + fs.existsSync(path.join(root, manifest.hooks)), + `missing hooks file: ${manifest.hooks}`, + ); + + for (const file of REQUIRED_COMMAND_FILES) { + assert.ok( + fs.existsSync(path.join(root, 'commands', file)), + `missing command file: commands/${file}`, + ); + } +}); diff --git a/tests/hooks.test.js b/tests/hooks.test.js index 53465dcd..f72a62cd 100644 --- a/tests/hooks.test.js +++ b/tests/hooks.test.js @@ -209,4 +209,37 @@ assert.equal(output.systemMessage, 'PONYTAIL:FULL'); assert.equal(output.hookSpecificOutput.hookEventName, 'SubagentStart'); assert.match(output.hookSpecificOutput.additionalContext, /PONYTAIL MODE ACTIVE — level: full/); +// Droid hook: assert the droid branch emits the badge plus hookSpecificOutput and writes state to ~/.factory/.ponytail-active. +const droidEnv = { + HOME: home, + USERPROFILE: home, + DROID_PLUGIN_ROOT: '/path/to/droid/plugin', + PONYTAIL_DEFAULT_MODE: 'full', +}; +const droidState = path.join(home, '.factory', '.ponytail-active'); + +try { fs.unlinkSync(droidState); } catch (e) {} + +result = run('ponytail-activate.js', droidEnv); +assert.equal(result.status, 0, result.stderr); +assert.equal(fs.readFileSync(droidState, 'utf8'), 'full'); +output = JSON.parse(result.stdout); +assert.equal(output.systemMessage, 'PONYTAIL:FULL'); +assert.equal(output.hookSpecificOutput.hookEventName, 'SessionStart'); +assert.match( + output.hookSpecificOutput.additionalContext, + /PONYTAIL MODE ACTIVE — level: full/, +); + +result = run( + 'ponytail-mode-tracker.js', + droidEnv, + JSON.stringify({ prompt: '/ponytail ultra' }), +); +assert.equal(result.status, 0, result.stderr); +assert.equal(fs.readFileSync(droidState, 'utf8'), 'ultra'); +output = JSON.parse(result.stdout); +assert.equal(output.systemMessage, 'PONYTAIL:ULTRA'); +assert.equal(output.hookSpecificOutput.hookEventName, 'UserPromptSubmit'); + console.log('hook compatibility checks passed');