diff --git a/.devin-plugin/plugin.json b/.devin-plugin/plugin.json new file mode 100644 index 0000000..90a7393 --- /dev/null +++ b/.devin-plugin/plugin.json @@ -0,0 +1,13 @@ +{ + "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" + }, + "homepage": "https://github.com/DietrichGebert/ponytail", + "repository": "https://github.com/DietrichGebert/ponytail", + "license": "MIT", + "keywords": ["yagni", "minimalism", "code-review", "productivity"] +} diff --git a/README.es.md b/README.es.md index 20b880b..a0f9566 100644 --- a/README.es.md +++ b/README.es.md @@ -15,7 +15,7 @@ Stars Release npm - Works with 14 agents + Works with 15 agents MIT license

@@ -35,6 +35,10 @@ --- +

+ Algo nuevo está por llegar, únete a la lista +

+ Lo conoces. Cola de caballo larga. Lentes ovalados. Lleva más tiempo en la empresa que el control de versiones. Le muestras cincuenta líneas; las mira, no dice nada, y las reemplaza por una. Ponytail lo pone dentro de tu agente de IA. @@ -194,6 +198,14 @@ Reutiliza el `gemini-extension.json` de este repo. Una diferencia: Antigravity c Lee `AGENTS.md` desde la raíz del proyecto, sin configuración. Copia [`AGENTS.md`](AGENTS.md) a tu proyecto, o ejecuta `codewhale` desde un checkout de este repo. Eso es todo. +### Devin CLI + +```bash +devin plugins install DietrichGebert/ponytail +``` + +Instala ponytail como plugin de Devin; los skills quedan disponibles como `/ponytail:ponytail`, `/ponytail:ponytail-review`, etc. + ### OpenClaw ```bash @@ -228,7 +240,7 @@ Qué archivos corresponden a qué agente: [Portabilidad de agentes](docs/agent-p | `/ponytail-debt` | Recolecta los atajos marcados con `ponytail:` que dejaste pendientes en un registro, para que "después" no se convierta en "nunca". | | `/ponytail-help` | Referencia rápida de los comandos anteriores. | -Los comandos requieren un host compatible con skills (Claude Code, Codex, OpenCode, Gemini, pi). En Codex son skills; se invocan con `@` (`@ponytail-review`). Los adaptadores de solo instrucciones (Cursor, Windsurf, Cline, Copilot, Kiro, Antigravity) cargan el ruleset permanente sin los comandos. +Los comandos requieren un host compatible con skills (Claude Code, Codex, Devin CLI, OpenCode, Gemini, pi, Swival). En Codex son skills; se invocan con `@` (`@ponytail-review`). Los adaptadores de solo instrucciones (Cursor, Windsurf, Cline, Copilot, Kiro, Antigravity) cargan el ruleset permanente sin los comandos. ## Desarrollo @@ -257,6 +269,17 @@ El código que nunca escribiste escala infinitamente. Cero bugs, cero CVEs, 100% **¿Por qué "ponytail"?** Ya sabes exactamente por qué. +## Patrocinadores + +

+ + + + GreenPT + + +

+ ## Licencia [MIT](LICENSE). La licencia más corta que funciona. diff --git a/README.ko.md b/README.ko.md index 2dc576c..df8469c 100644 --- a/README.ko.md +++ b/README.ko.md @@ -15,7 +15,7 @@ Stars Release npm - Works with 14 agents + Works with 15 agents MIT license

@@ -35,6 +35,10 @@ --- +

+ 새로운 것이 다가오고 있습니다, 대기자 명단 신청 +

+ 이런 사람, 다들 알 거다. 긴 포니테일에 타원형 안경. 버전 관리 시스템보다 회사에 오래 있었다. 코드 쉰 줄을 들이밀면 잠깐 보더니, 말없이 한 줄로 바꿔 놓는다. Ponytail은 그를 당신의 AI 에이전트 안에 앉혀 둔다. @@ -213,6 +217,14 @@ Swival도 프로젝트 루트의 `AGENTS.md`와 전역의 `~/.config/swival/AGEN 명령줄에서는 `$` 접두사로 스킬을 명시적으로 켠다. 예: `$ponytail-review`. +### Devin CLI + +```bash +devin plugins install DietrichGebert/ponytail +``` + +ponytail을 Devin 플러그인으로 설치한다. 스킬은 `/ponytail:ponytail`, `/ponytail:ponytail-review` 등으로 쓸 수 있다. + ### OpenClaw ```bash @@ -248,7 +260,7 @@ Codex 확장을 쓰는 VS Code는 이 저장소가 함께 싣는 `AGENTS.md`를 | `/ponytail-gain` | 벤치마크로 잰 효과 스코어보드(코드 절감, 비용 절감, 속도 향상)를 보여 준다. | | `/ponytail-help` | 위 명령들의 빠른 참조. | -명령들은 스킬을 지원하는 호스트가 있어야 돈다(Claude Code, Codex, OpenCode, Gemini, pi). Codex에선 스킬이라 `@`로 부른다(`@ponytail-review`). 지시문 전용 어댑터(Cursor, Windsurf, Cline, Copilot, Kiro, Antigravity)는 명령 없이 늘 켜진 룰셋만 불러온다. +명령들은 스킬을 지원하는 호스트가 있어야 돈다(Claude Code, Codex, Devin CLI, OpenCode, Gemini, pi, Swival). Codex에선 스킬이라 `@`로 부른다(`@ponytail-review`). 지시문 전용 어댑터(Cursor, Windsurf, Cline, Copilot, Kiro, Antigravity)는 명령 없이 늘 켜진 룰셋만 불러온다. ## Development @@ -277,6 +289,17 @@ OpenClaw 스킬 패키지(`.openclaw/skills/`)는 `skills/`에서 생성된다. **왜 하필 "ponytail"인가?** 당신은 이유를 정확히 안다. +## Sponsors + +

+ + + + GreenPT + + +

+ ## License [MIT](LICENSE). 돌아가는 가장 짧은 라이선스. diff --git a/README.md b/README.md index ff84b2f..597adf8 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Stars Release npm - Works with 15 agents + Works with 16 agents MIT license

@@ -35,6 +35,10 @@ --- +

+ Something's coming, join the waitlist +

+ You know him. Long ponytail. Oval glasses. Has been at the company longer than the version control. You show him fifty lines; he looks at them, says nothing, and replaces them with one. Ponytail puts him inside your AI agent. @@ -221,6 +225,14 @@ Swival also reads `AGENTS.md` from the project root and `~/.config/swival/AGENTS On the command line, use a `$` prefix to explicitly activate a skill. For example: `$ponytail-review`. +### Devin CLI + +```bash +devin plugins install DietrichGebert/ponytail +``` + +Installs ponytail as a Devin plugin; skills are available as `/ponytail:ponytail`, `/ponytail:ponytail-review`, and so on. + ### OpenClaw ```bash @@ -251,6 +263,7 @@ Which files map to which agent: [Agent portability](docs/agent-portability.md). |------|---------| | Claude Code | `/plugin remove ponytail` | | Codex | `codex plugin remove ponytail` | +| Devin CLI | `devin plugins remove ponytail` | | Pi agent | `pi uninstall ponytail` | | Cursor / Windsurf / Cline / etc. | Delete the copied rule file | @@ -267,7 +280,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, OpenCode, Gemini, pi, Swival, Hermes Agent). 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). 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. ## Development @@ -296,6 +309,17 @@ The code you never wrote scales infinitely. Zero bugs, zero CVEs, 100% uptime si **Why "ponytail"?** You know exactly why. +## Sponsors + +

+ + + + GreenPT + + +

+ ## License [MIT](LICENSE). The shortest license that works. diff --git a/assets/logo-greenpt-dark.svg b/assets/logo-greenpt-dark.svg new file mode 100644 index 0000000..cbcfd6a --- /dev/null +++ b/assets/logo-greenpt-dark.svg @@ -0,0 +1,27 @@ + + + logo-greengpt-white + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/logo-greenpt.svg b/assets/logo-greenpt.svg new file mode 100644 index 0000000..b415210 --- /dev/null +++ b/assets/logo-greenpt.svg @@ -0,0 +1,27 @@ + + + logo-greengpt-black + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/waitlist-banner-es.png b/assets/waitlist-banner-es.png new file mode 100644 index 0000000..ca30a06 Binary files /dev/null and b/assets/waitlist-banner-es.png differ diff --git a/assets/waitlist-banner-ko.png b/assets/waitlist-banner-ko.png new file mode 100644 index 0000000..6407e95 Binary files /dev/null and b/assets/waitlist-banner-ko.png differ diff --git a/assets/waitlist-banner.png b/assets/waitlist-banner.png new file mode 100644 index 0000000..fc7087b Binary files /dev/null and b/assets/waitlist-banner.png differ diff --git a/benchmarks/benchmark-local.py b/benchmarks/benchmark-local.py index f1a57de..8ffe58a 100644 --- a/benchmarks/benchmark-local.py +++ b/benchmarks/benchmark-local.py @@ -154,6 +154,8 @@ def main(): parsed_url = urllib.parse.urlparse(args.ollama_url) if parsed_url.scheme not in ("http", "https"): parser.error(f"Invalid --ollama-url scheme: '{parsed_url.scheme}'. Only 'http' and 'https' are supported.") + if not parsed_url.netloc: + parser.error(f"--ollama-url must include a host, e.g. http://localhost:11434 (got '{args.ollama_url}').") run(args.model, args.repeat, args.ollama_url) diff --git a/benchmarks/loc.js b/benchmarks/loc.js index 3640fa0..93e13dd 100644 --- a/benchmarks/loc.js +++ b/benchmarks/loc.js @@ -4,7 +4,9 @@ module.exports = (output) => { const text = String(output || ''); const blocks = [...text.matchAll(/```[a-zA-Z0-9_+-]*\n([\s\S]*?)```/g)].map((m) => m[1]); - const code = blocks.length ? blocks.join('\n') : text; + // Drop /* ... */ block comments before counting; the line filter below only + // caught `*`-aligned JSDoc, so plain block comments were miscounted as code. + const code = (blocks.length ? blocks.join('\n') : text).replace(/\/\*[\s\S]*?\*\//g, ''); const loc = code .split('\n') .map((l) => l.trim()) diff --git a/benchmarks/loc.test.js b/benchmarks/loc.test.js new file mode 100644 index 0000000..de30281 --- /dev/null +++ b/benchmarks/loc.test.js @@ -0,0 +1,22 @@ +// Regression guard for loc.js comment handling. Run: node loc.test.js +const assert = require('assert'); +const loc = require('./loc.js'); + +const score = (src) => loc(src).score; + +let pass = 0; +const cases = [ + // /* ... */ block comments must not count as code, whether or not the + // continuation lines are *-aligned (the old filter only caught JSDoc style). + ['plain block comment not counted', score('```js\nfunction f() {\n /* explain\n the rest */\n return 1;\n}\n```'), 3], + ['jsdoc block comment not counted', score('```js\nfunction g() {\n /*\n * explain\n */\n return 2;\n}\n```'), 3], + ['inline block comment keeps its code line', score('```js\nconst x = 1; /* note */\nconst y = 2;\n```'), 2], + ['line comments still stripped', score('```js\n// header\nconst x = 1;\n```'), 1], + ['plain code unchanged', score('```js\nconst a = 1;\nconst b = 2;\n```'), 2], +]; +for (const [name, got, want] of cases) { + assert.strictEqual(got, want, `FAILED: ${name} (got ${got}, want ${want})`); + console.log(`ok - ${name}`); + pass++; +} +console.log(`\n${pass}/${cases.length} passed`); diff --git a/docs/platform-native.md b/docs/platform-native.md index afb4cfe..11e7d6e 100644 --- a/docs/platform-native.md +++ b/docs/platform-native.md @@ -86,6 +86,44 @@ const debounce = (fn, ms) => (...args) => { clearTimeout(t); t = setTimeout(() = --- +## Swift / SwiftUI + +UI components people reach for a library or a custom view for. + +| You think you need | What the platform has | +|---|---| +| Date/time picker library | `DatePicker` | +| Color picker library | `ColorPicker` | +| Search bar + filtering | `.searchable(text:)` | +| Pull-to-refresh library | `.refreshable { }` | +| Swipe-to-delete / row actions | `.swipeActions { }` | +| Async image loading + cache | `AsyncImage` | +| Charting library | Swift Charts (`import Charts`) | +| Markdown rendering | `Text(...)` markdown / `AttributedString(markdown:)` | +| Share sheet wrapper | `ShareLink` | +| Loading spinner | `ProgressView()` | +| Photo picker | `PhotosPicker` | +| Map SDK (basic) | `Map` (MapKit for SwiftUI) | +| Grid layout library | `Grid` / `LazyVGrid` | + +Frameworks and stdlib that wrappers wrap. + +| You think you need | What the platform has | +|---|---| +| JSON library (SwiftyJSON) | `Codable` + `JSONDecoder` / `JSONEncoder` | +| HTTP client (Alamofire, simple use) | `URLSession` async/await; Alamofire earns it for complex retry/multipart at scale | +| Date/number/currency formatting | `.formatted()` / `FormatStyle` | +| Regex library | Swift regex literals + `Regex` | +| Crypto library (CryptoSwift) | `CryptoKit` | +| Keychain wrapper | Security `SecItem`; a few lines, not a dependency | +| Persistence / ORM | `SwiftData`, or `@AppStorage` for small key-values | +| Logging library | `Logger` (`os.log`) | +| UUID / Base64 helpers | `UUID()`, `Data(...).base64EncodedString()` | +| Image downsampling | ImageIO `CGImageSourceCreateThumbnailAtIndex` | +| Combine wrappers for async | async/await + `AsyncSequence` | + +--- + ## Node.js Standard Library Packages that wrap Node built-ins. diff --git a/scripts/check-versions.js b/scripts/check-versions.js index e7006fb..5c6e04c 100644 --- a/scripts/check-versions.js +++ b/scripts/check-versions.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -// Version-consistency guard. Ponytail declares its version in six files across -// four host ecosystems, and every release bumps all of them by hand. +// Version-consistency guard. Ponytail declares its version in seven files across +// five host ecosystems, and every release bumps all of them by hand. // // tests/gemini-extension.test.js already checks the four plugin manifests agree // with each other, but that can't catch the failure mode that shipped in v4.8.0: @@ -19,12 +19,13 @@ const PINNED_SEMVER = /^\d+\.\d+\.\d+$/; // Every file that declares the project version, and who reads it. Add new host // manifests here so a future ecosystem can't drift unnoticed. const VERSION_FILES = [ - '.claude-plugin/plugin.json', // Claude Code plugin — what users install - '.codex-plugin/plugin.json', // Codex plugin - '.github/plugin/plugin.json', // Copilot plugin - 'gemini-extension.json', // Gemini CLI extension - 'package.json', // pi-package / repo root - 'ponytail-mcp/package.json', // MCP server (private, internal-only) + '.claude-plugin/plugin.json', // Claude Code plugin — what users install + '.codex-plugin/plugin.json', // Codex plugin + '.devin-plugin/plugin.json', // Devin CLI plugin + '.github/plugin/plugin.json', // Copilot plugin + 'gemini-extension.json', // Gemini CLI extension + 'package.json', // pi-package / repo root + 'ponytail-mcp/package.json', // MCP server (private, internal-only) ]; function readVersion(relPath) {