{"id":51403639,"url":"https://github.com/chrisryugj/fable-ish","last_synced_at":"2026-07-04T09:04:53.307Z","repository":{"id":364810427,"uuid":"1269300580","full_name":"chrisryugj/fable-ish","owner":"chrisryugj","description":"Fable-style verification gate for Claude Code: don't say done until it's verified. Port of the Codex plugin by Agent Korea (플라잉따릉이).","archived":false,"fork":false,"pushed_at":"2026-06-22T07:57:49.000Z","size":71,"stargazers_count":35,"open_issues_count":0,"forks_count":10,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-22T09:31:34.885Z","etag":null,"topics":["ai-agents","anthropic","claude","claude-code","claude-code-plugin","codex","developer-tools","fable","hooks","verification"],"latest_commit_sha":null,"homepage":"https://github.com/chrisryugj/fable-ish#readme","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chrisryugj.png","metadata":{"files":{"readme":"README-EN.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-14T14:44:39.000Z","updated_at":"2026-06-22T07:58:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/chrisryugj/fable-ish","commit_stats":null,"previous_names":["chrisryugj/fable-ish"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/chrisryugj/fable-ish","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisryugj%2Ffable-ish","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisryugj%2Ffable-ish/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisryugj%2Ffable-ish/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisryugj%2Ffable-ish/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chrisryugj","download_url":"https://codeload.github.com/chrisryugj/fable-ish/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisryugj%2Ffable-ish/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35115770,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-04T02:00:05.987Z","response_time":113,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agents","anthropic","claude","claude-code","claude-code-plugin","codex","developer-tools","fable","hooks","verification"],"created_at":"2026-07-04T09:04:52.760Z","updated_at":"2026-07-04T09:04:53.299Z","avatar_url":"https://github.com/chrisryugj.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# fable-ish\n\n**Don't say \"done\" until it's verified.**\n\n[![license](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)\n[![platform](https://img.shields.io/badge/platform-Claude%20Code-7c3aed.svg)](https://claude.com/claude-code)\n\n\u003e *When the AI says \"tests pass,\" did it actually run them?*\n\n`fable-ish` reproduces, as **Claude Code** hooks, the *verification discipline* shown by **Fable** — Anthropic's recent coding model. Fable's strength isn't \"a smart model writes good code\"; it's the **loop**: it resets its exit criteria to match each task's risk, and refuses to call work done until those criteria pass an *observable* proof. fable-ish imitates that discipline with three lightweight hooks (hence the `-ish`).\n\n[한국어](./README.md)\n\n\u003e **Origin \u0026 port.** The original `fable-ish` was built for **Codex** by **플라잉따릉이 (Agent Korea)**, who reverse-engineered Fable's behavior into a plugin. This repository is a port of that plugin to **Claude Code** (port by chrisryugj). The plugin name, skill, and workflow are preserved from the original.\n\n---\n\n## 🔁 The Fable loop, in one line\n\n\u003e Set the goal → gather evidence → define the work unit → **set exit criteria** → implement → **verify** → hunt for counter-examples → re-tune the criteria → exit if they pass, else repeat.\n\nThe point isn't a *fixed checklist* (\"tests pass\") — it's **dynamic exit criteria**. Low risk? One static check is enough. High risk (auth, DB, deploy)? A stronger proof becomes the exit bar.\n\n\u003e The loop as a whole is **guidance the skill provides**. What the hooks *mechanically enforce* is just the last part — **refusing a \"done\" with no verification evidence** (the Stop gate), plus re-engaging a turn that only promised work. The other steps (inspect context, name the work unit, hunt counter-examples, …) are advised, not enforced.\n\n---\n\n## 💡 What it does\n\nLLMs often report \"all done\" without verifying. fable-ish adds **friction** to that habit using lifecycle hooks:\n\n- **🏷️ Task classification** — sorts each incoming request into `quick` / `normal` / `deep` / `blocked` and injects the matching verification expectation. Destructive / secret-exposing prompts are stopped at the *input* stage, not at the end.\n- **🛡️ Risky-action blocking** — denies a small set of destructive local operations before they run: `rm -rf`, recursive `Remove-Item`, `git push`, `terraform`/`pulumi destroy`, secret-output commands, and edits to secret files (`.env`/`.pem`/`id_rsa`). Deployment, migration, and publish commands are intentionally **not** blocked — fable-ish is a verification gate, not a deploy gate. This is a heuristic first line — back it with `permissions.deny` for hard enforcement (see the [design note](#️-design-note--guardrails-and-hard-enforcement)).\n- **📒 Evidence tracking** — records which files changed, which verification commands ran, and whether anything failed, in a small JSON ledger. It even tracks whether a proof actually *covered the changed files* (`direct` / `generic` / `uncertain`).\n- **🚦 Completion gate** — if code changed but no successful verification was observed, it sends Claude back to verify at stop time (capped at two blocks to avoid loops).\n\nOne core rule: **never claim verification that was not observed.**\n\n---\n\n## ⚡ Installation\n\nThis repository is both a single plugin and its marketplace (`.claude-plugin/marketplace.json`).\n\n**Locally, right now (just clone it):**\n\n```text\n/plugin marketplace add /path/to/fable-ish\n/plugin install fable-ish\n```\n\n**After it's on GitHub (once you push the repo):**\n\n```text\n/plugin marketplace add chrisryugj/fable-ish\n/plugin install fable-ish\n```\n\nAfter installing, **restart Claude Code**. Plugin-bundled hooks are **not trusted automatically** — open `/hooks`, review the definitions, and trust them before relying on the gate.\n\n\u003e **Requirements**: the hooks are standard-library Python scripts; `python3` must be on PATH. On Windows, use `python` if `python3` is unavailable, or adjust the `command` entries in `hooks/hooks.json`.\n\n---\n\n## ▶️ Usage — install once, then it just flows\n\nOnce installed and trusted via `/hooks`, it's **automatic** — there's nothing to turn on.\n\n- **Automatic (hooks):** every prompt is classified `quick`/`normal`/`deep`/`blocked`, and if code changed without verification, the Stop gate sends Claude back. You do nothing.\n- **Explicit (skill):** invoke it directly with the `/fable-ish \u003ctask\u003e` slash command, with natural language like *\"implement this end-to-end with fable-ish\"*, or let Claude call the skill on its own for complex/verification-heavy work. (The `fablish` typo resolves to the same skill.)\n\nIf hooks are disabled or untrusted, the skill still works as reusable instructions, but the mechanical gate won't run.\n\n\u003e **Note**: the blocking hooks are heuristic (bypassable), so treat them as a *first line*. For unbypassable hard enforcement, also set native `permissions.deny` — example in [`examples/claude-permissions.example.json`](./examples/claude-permissions.example.json), explained in the [design note](#️-design-note--guardrails-and-hard-enforcement).\n\n---\n\n## 🔧 How it works\n\n| Hook | Role |\n|------|------|\n| `UserPromptSubmit` | Classifies the request as `quick`/`normal`/`deep`/`blocked` and injects short context. `blocked` (secret/destructive requests) is stopped with `decision:block` |\n| `PreToolUse` / `PermissionRequest` | Deny a small set of destructive Bash/PowerShell commands (`rm -rf`, recursive `Remove-Item`, `git push`, `terraform`/`pulumi destroy`, secret output …) and secret-file edits (`.env`/`.pem`/`id_rsa`) before they run or are approved. Deploy/migration/publish commands are not blocked |\n| `PostToolUse` (+ `PostToolUseFailure`) | On **both success and failure** of Bash/PowerShell/edit tools, records changed files/paths, verification commands, a coverage relation (`direct`/`generic`/`uncertain`), and failures in a JSON ledger (under `CLAUDE_PLUGIN_DATA`, OS-temp fallback). A verification that arrives via the failure event is never logged as a pass |\n| `Stop` | Re-engages when work changed files without successful verification, or when the turn only *promised* work without doing it (capped at two blocks; yields immediately when `stop_hook_active` is set) |\n\nThe skill (`skills/fable-ish/SKILL.md`) is the human-readable workflow layer: mode-by-mode work loops, the verification ladder, and the final-report format.\n\n---\n\n## 🛡️ Design note — guardrails and hard enforcement\n\nfable-ish ports the original's `PreToolUse` / `PermissionRequest` hooks that block risky commands and secret-file edits. **But this is a heuristic first line, not a security boundary.**\n\nJudging shell *intent* with regex is inherently incomplete. Variable expansion, quoting, globs, and encoding can slip past the surface pattern (e.g. `rm -r -f` split flags, `$VAR -rf`, base64-piped `eval`), and it can also over-block legitimate work. So treat this layer as a *tripwire for accidental mistakes*.\n\nFor **unbypassable hard enforcement**, also set Claude Code's native `permissions.deny`, where commands are parsed and a user approval step applies. A ready-to-use example is in [`examples/claude-permissions.example.json`](./examples/claude-permissions.example.json):\n\n```json\n{\n  \"permissions\": {\n    \"deny\": [\n      \"Read(./.env)\",\n      \"Write(./.env)\",\n      \"Bash(rm -rf:*)\",\n      \"Bash(git push:*)\",\n      \"Bash(npm publish:*)\"\n    ]\n  }\n}\n```\n\nFor real isolation, add sandboxing / permission modes (avoid `bypassPermissions`) / approvals / review. In short: fable-ish's blocking is the *first line*, `permissions.deny` is the *hard enforcement*, and the core is still making sure work is **verified before it's reported done**.\n\nFor real isolation, rely on sandboxing, permission modes (avoid `bypassPermissions`), approvals, and review — not string matching. fable-ish focuses on the part a hook is actually good at: making sure work is *verified* before it's reported done.\n\n---\n\n## ⚠️ Limits\n\nThe hooks are guardrails and a discipline aid, not a complete security boundary.\n\n- Multiple matching hooks can run concurrently.\n- `PreToolUse` blocking does not intercept every possible shell path — it's a heuristic that variables, quoting, and encoding can bypass.\n- `PostToolUse` cannot undo side effects from a completed command.\n- Task classification is heuristic; treat the mode as a hint, not a guarantee.\n- Hooks require review and trust before running.\n- Stop blocking is intentionally capped (two blocks) to avoid infinite loops.\n- The `permissions.deny` profile is provided as an example only; this plugin does not silently install it.\n\nFor stronger enforcement, add sandboxing, approvals, tests, linters, and review policies.\n\n---\n\n## 🚫 Excluded by design\n\n- No MCP server.\n- No app integration.\n- No external API.\n- No web server or UI.\n- No database.\n- No background worker.\n- No LLM classifier (classification is regex heuristics).\n- No GitHub, Slack, Gmail, or Notion integration.\n- No complex policy engine.\n\n---\n\n## 🧪 Development / Verification\n\nFrom the plugin root:\n\n```bash\npython3 -m json.tool .claude-plugin/plugin.json\npython3 -m json.tool hooks/hooks.json\npython3 -m py_compile hooks/*.py scripts/*.py\npython3 tests/test_hooks.py\n```\n\nSample hook inputs can be piped into each hook script with `CLAUDE_PLUGIN_DATA` set to a temporary directory.\n\n---\n\n## 🙏 Credits\n\n- **Original author (Codex plugin):** [플라잉따릉이 (Agent Korea)](https://github.com/) — verification-gate design and original implementation\n- **Claude Code port:** chrisryugj\n\nThe original was built as a Codex plugin; this repository adapts it to Claude Code (hooks, manifest, tooling) without renaming the plugin or its workflow.\n\n---\n\n## 📄 License\n\n[MIT](./LICENSE) — © 플라잉따릉이 (Agent Korea) and contributors.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisryugj%2Ffable-ish","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrisryugj%2Ffable-ish","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisryugj%2Ffable-ish/lists"}