{
  "markdown": "<div align=\"center\">\n  <img src=\"branding/lockup.svg\" alt=\"goalkeeper\" width=\"360\">\n  <br>\n  <h3>Set durable goals. Approve at the gate.</h3>\n  <p>Contract-driven autonomous goal execution for <a href=\"https://docs.claude.com/en/docs/claude-code/overview\">Claude Code</a>.<br>A subagent judge gates completion against an explicit Definition of Done.</p>\n  <p>\n    <a href=\"https://github.com/bonfire-systems/goalkeeper/actions/workflows/test.yml\"><img alt=\"test\" src=\"https://github.com/bonfire-systems/goalkeeper/actions/workflows/test.yml/badge.svg\"></a>\n    <img alt=\"license\" src=\"https://img.shields.io/badge/license-MIT-0E7C66\">\n    <img alt=\"version\" src=\"https://img.shields.io/badge/version-0.1.11-0E7C66\">\n    <img alt=\"claude-code\" src=\"https://img.shields.io/badge/claude--code-plugin-0E7C66\">\n  </p>\n</div>\n\n<!--\nDEMO SLOT — when you record an asciinema cast, replace this comment with:\n\n<div align=\"center\">\n  <a href=\"https://asciinema.org/a/XXXXXX\">\n    <img src=\"https://asciinema.org/a/XXXXXX.svg\" alt=\"goalkeeper reject-then-approve demo\" width=\"720\">\n  </a>\n</div>\n\nSwap XXXXXX for the cast ID returned by `asciinema upload demo.cast`.\nUntil then, the example below is the canonical \"real reject-cycle\" reference.\n-->\n\n> **Real reject-cycle, 3 minutes wall-clock.** Goalkeeper caught a `MAX_RUNTIME_MS = 9999 // TODO` sentinel placeholder in a benchmark test where the validator passed both rounds. Round 1: validator green, judge **reject** on DoD #3 + #7 with a 3-step fix-list. Round 2: real threshold (500ms = 10x measured baseline) with justification, validator green, judge **approve**. Full transcript with quoted verdicts: [`docs/demo.md`](./docs/demo.md). Long-form writeup on [dev.to](https://dev.to/itsuzef/the-judge-gate-why-a-passing-validator-isnt-a-finished-feature-f2f).\n\n---\n\n## How it differs\n\n|  | OpenAI Codex `/goal` | Ralph loop | **goalkeeper** |\n|---|---|---|---|\n| Durable goal across many turns | ✓ | ✓ | ✓ |\n| Validator back-pressure | optional | core | core |\n| **Independent judge gate against an explicit Definition of Done** | — | — | **core** |\n| Anti-placeholder rule (stubs / `.todo` / `it.only` auto-reject) | — | informal | **enforced** |\n| Linear chains of role-specific goals with judge-gated handoff | — | — | **`/goal-chain`** |\n| Auto-pauses after N consecutive rejections | — | — | **5 (configurable)** |\n| Append-only checkpoint log per goal | — | informal | enforced |\n| Spec lives in | CLI prompt | `PROMPT.md` | `contract.md` (validated against JSON Schema) |\n| Pre-existing validator failures distinguished from goal-caused | — | — | **`validator_baseline_*`** |\n\nInspired by [OpenAI Codex `/goal`](https://developers.openai.com/codex/use-cases/follow-goals) and Geoffrey Huntley's [Ralph loop](https://ghuntley.com/ralph/), with one key addition: the judge.\n\n## Why\n\nCodex `/goal` runs autonomously until a stop-condition is met. The Ralph loop runs `while :; do cat PROMPT.md | claude-code; done` and leans on validators (compile, test, lint) to back-pressure the model. Both work, but both have the same failure mode: **a passing validator is not the same as a finished feature**. Tests can pass on stubs. Linters can pass on `.todo`s. Codex can declare victory the moment its stop-condition string matches.\n\ngoalkeeper adds a second gate. After your validator passes, a fresh subagent — independent context, no rationalizations from the executing agent — reviews the diff and the progress log against your written Definition of Done, and either approves or returns a structured fix-list. After 5 rejections it pauses and asks for human help.\n\n## Install\n\nAdd the goalkeeper marketplace, then install the plugin:\n\n```bash\n# inside Claude Code\n/plugin marketplace add bonfire-systems/goalkeeper\n/plugin install goalkeeper@goalkeeper\n```\n\nSkills become available under the `goalkeeper:` namespace. Invoke as `/goalkeeper:goal \"<objective>\"`, `/goalkeeper:goal-prep`, `/goalkeeper:goal-judge`, etc. (Most users alias the namespace away — see \"Aliasing\" below.)\n\n### Aliasing the namespace\n\nIf you primarily use goalkeeper and want shorter commands, add aliases to `~/.claude/settings.json`:\n\n```json\n{\n  \"aliases\": {\n    \"/goal\": \"/goalkeeper:goal\",\n    \"/goal-prep\": \"/goalkeeper:goal-prep\",\n    \"/goal-pause\": \"/goalkeeper:goal-pause\",\n    \"/goal-resume\": \"/goalkeeper:goal-resume\",\n    \"/goal-clear\": \"/goalkeeper:goal-clear\",\n    \"/goal-judge\": \"/goalkeeper:goal-judge\",\n    \"/goal-chain\": \"/goalkeeper:goal-chain\"\n  }\n}\n```\n\n## Quick start\n\n```\n/goal-prep \"Migrate the test suite from Jest to Vitest\"\n```\n\ngoalkeeper reads your repo, asks a few targeted questions (objective, definition-of-done, validator command, non-goals), writes `.claude/goals/<slug>/contract.md`, and offers to start. Once running, it:\n\n1. Works in checkpoints (logged to `log.md`).\n2. Runs your validator after each checkpoint.\n3. When the validator passes, spawns a subagent **judge** to check Definition of Done.\n4. Judge approves → done. Judge rejects → fix-list logged, work continues. After 5 rejections → paused for you.\n\nCheck status anytime:\n\n```\n/goal\n```\n\n## Commands\n\n| Command | What it does |\n|---|---|\n| `/goal \"<objective>\"` | Start (or resume) a goal. Auto-routes to `/goal-prep` if no contract exists yet. |\n| `/goal` | Show status of the active goal: last checkpoint, validator state, rejection count. |\n| `/goal-prep \"<rough idea>\"` | Interactively draft a contract — the highest-leverage step. Surveys the repo and uses targeted questions to lock in a precise spec. |\n| `/goal-pause` | Pause without losing state (\"I'll be right back\" — keeps the active slot). Pending wakeups become no-ops. |\n| `gk park [slug] --needs \"<what>\"` | Park a goal on a human-gated blocker (credential, consent, decision). Frees the active slot so other goals run; `gk status` lists the parked queue with each goal's unblock instruction. The goal is parked, never the agent. |\n| `/goal-resume` | Resume a paused or parked goal (`gk resume [slug]`). If resuming with rejections on the clock, asks whether to reset the counter. Re-arms a `waiting` chain. |\n| `/goal-clear` | Stop and archive the goal to `.claude/goals/_archive/`. Files are moved, never deleted. |\n| `/goal-judge` | Run the judge advisorily on the active goal (no state change). Useful as a manual sanity check. |\n| `/goal-chain \"<file>\"` | Run a linear sequence of goals; the judge gates progression between them. |\n\n## Contract format\n\nA contract is a markdown file at `.claude/goals/<slug>/contract.md` with frontmatter:\n\n```yaml\n---\nslug: jest-to-vitest-migration\nobjective: Migrate the test suite from Jest to Vitest with no behavioral regressions and a measurable speed improvement.\nnon_goals:\n  - Do not rewrite test logic\n  - Do not change source code under src/\ndefinition_of_done:\n  - All test files import from \"vitest\" instead of \"@jest/globals\"\n  - jest.config.* is removed; vitest.config.ts exists with equivalent coverage thresholds\n  - \"pnpm test\" runs the full suite under Vitest with 100% of previously-passing tests still passing\n  - Wall-clock test runtime improves by at least 20% vs the Jest baseline\nvalidator:\n  command: pnpm test --run && pnpm exec node scripts/check-no-jest-refs.mjs\n  success: exit_zero\n  timeout_seconds: 1200\ncheckpoint_cadence: every 5 file edits OR every 20 minutes\nmax_rejections: 5\njudge_mode: subagent\nwakeup_seconds: 270\n---\n\n## Context\n<freeform body — file pointers, constraints, hints, anti-placeholder reminders>\n```\n\nThe body of the contract is your \"PROMPT.md\" — file pointers, constraints, hints. See [`examples/`](./examples/) for full contracts.\n\nSchema: [`schemas/contract.schema.json`](./schemas/contract.schema.json).\n\n### Field reference\n\n- **slug** — kebab-case directory name. Must be unique within `.claude/goals/`.\n- **objective** — one sentence, what success looks like.\n- **non_goals** — explicit out-of-scope items. Judge rejects on violation even if DoD is met.\n- **definition_of_done** — what the judge grades against. Be specific; \"works correctly\" is not a DoD.\n- **validator.command** — runs after each checkpoint. Necessary but not sufficient.\n- **validator.success** — `exit_zero` (default) or `regex:<pattern>` matched against stdout.\n- **checkpoint_cadence** — agent's guidance for how often to log + validate.\n- **max_rejections** — default 5. After this many judge rejections, pauses for human.\n- **judge_mode** — `subagent` (default, gate-quality) or `inline` (cheap, advisory only).\n- **wakeup_seconds** — between-iteration delay. Tune to validator runtime; if unset, the model picks per the harness's ScheduleWakeup guidance.\n- **diff_excludes** — additional pathspec globs the judge should ignore. Appended to defaults (lockfiles, `dist/`, `build/`, `coverage/`, IDE files). Add per-repo noise like generated migrations or vendor trees.\n\n## Chains\n\nA chain is an ordered list of slugs. The judge gates progression — only after approval does the next goal start.\n\n```markdown\n---\nname: bonfire-bass-rust-port\n---\n\n1. port-dsp-core-to-rust\n2. wire-up-ffi-shim\n3. swap-cpp-for-rust-in-host\n4. delete-cpp-tree\n```\n\nRun with:\n\n```\n/goal-chain \".claude/goals/chains/bonfire-bass.md\"\n```\n\nEach slug must already have a contract at `.claude/goals/<slug>/contract.md`. Run `/goal-prep` for each before starting the chain.\n\n## Multi-role goals\n\nMost non-trivial goals span multiple roles — backend changes, UI wiring, migrations, tests, docs. Goalkeeper deliberately keeps just two agent slots (executor + judge) and stays framework-agnostic about how you spawn specialists. There are two patterns for getting role-shaped work through goalkeeper, and the right choice depends on how decoupled the roles are.\n\n### Pattern A — chain of role-specific contracts (default)\n\nFrame each role as its own contract with its own Definition of Done, validator, and judge. Compose them with `/goal-chain`. The judge gates progression: backend's judge has to approve before UI starts; UI's judge has to approve before migrations.\n\n```markdown\n---\nname: stripe-integration\n---\n\n1. stripe-api-routes\n2. stripe-db-migration\n3. stripe-checkout-ui\n4. stripe-e2e-tests\n```\n\nEach slug has its own `.claude/goals/<slug>/contract.md` with a focused DoD (\"Stripe webhook signature verification works\", \"checkout component handles 3DS challenge\", etc.) and a focused validator (`pnpm test packages/api`, `pnpm test packages/web`, etc.). See [`examples/chain.md`](./examples/chain.md) for a worked example.\n\n**Choose Pattern A when:** roles are sequenceable. One role's work produces the artifacts the next role consumes. The dependency graph is linear or close to it. You want the judge to gate-keep at each role boundary so a sloppy backend can't silently corrupt the UI step.\n\n**Tradeoff:** chains force upfront decomposition. You have to know the boundaries before you start. If the boundaries shift mid-flight, you have to clear the chain and re-plan.\n\n### Pattern B — in-goal `specialists:` orchestration (proposed for v0.2)\n\nFor tightly-coupled cross-role work — same files, can't be sequenced — Pattern A creates artificial mid-flight pauses. Pattern B is the escape hatch: a single contract with a `specialists:` field that lists the roles available, plus role-specific system prompts. The executing agent is the orchestrator and dispatches subtasks to specialist subagents (Claude Code general-purpose agents with custom system prompts, your own `subagent_type` definitions, or MCP-based agents — goalkeeper doesn't ship specialist definitions).\n\n```yaml\nspecialists:\n  - role: backend\n    system_prompt: \"You are a senior Node.js engineer. ...\"\n  - role: ui\n    system_prompt: \"You are a senior React engineer. ...\"\n```\n\nThe judge still gates final approval against the unified DoD; the executing agent's log records which specialist did what.\n\n**Status: proposed for v0.2 — not yet implemented.** The `specialists:` field is not in `schemas/contract.schema.json` yet. If you need this today, write your own orchestration in the body of the contract and have the executing agent spawn subagents using whatever Claude Code mechanism you prefer; goalkeeper observes via the log and the judge gates the result against the unified DoD.\n\n**Choose Pattern B when:** specialists genuinely have to interleave on the same files. A frontend change requires a simultaneous backend change, both committed together, both tested together. Pattern A would create a contract that artificially pauses the work mid-flight.\n\n**Tradeoff:** the executing agent owns coordination state, and the judge has to reason about multiple specialists' contributions in a single DoD pass. Easier bugs to hide than in chains.\n\n### Default: prefer A unless you can articulate why A doesn't fit\n\nIf you can list the roles in dependency order and each role can complete independently before the next starts, use Pattern A. Reach for Pattern B only when the roles must touch the same code in the same edit.\n\n## Missions — supervised iterative arcs (v0.2)\n\nFor multi-goal work where each next goal's shape is informed by the prior goal's actual output, the supervisor primitive sits one level above goals. Where the judge gates a *goal* against its Definition of Done, the supervisor gates the *mission* against its charter and adaptively decides what goal to run next.\n\nAuthoring a mission charter at `.claude/mission.md`:\n\n```markdown\n# Mission: v2-cutover\n\n## Objective\nShip V2 cold-inbound to production safely.\n\n## Success condition\nAll 6 cutover gates pass on a 2-week shadow window + 5%→25%→100% canary clean.\n\n## Constraints\n- Never send V2 outbound during shadow.\n- Any cutover gate fail → fix + restart shadow.\n- Max 2 weeks elapsed before user checkpoint.\n\n## Legal next-goal shapes\n- shadow-infra — assemble pipeline + tee + writer\n- gate-fix — patch a specific failing axis from shadow data\n- canary-rollout — % hash routing\n- rollback — revert canary % to 0\n\n## Done is not\n- Just \"validator passes.\" Done is \"6 gates pass on shadow + canary green at 100%.\"\n```\n\nAfter a goal completes, `/goalkeeper:goal-supervisor` reads the charter + the prior goal's artifacts and decides:\n\n- **PROCEED** — drafts the next goal's objective (informed by what the prior goal actually produced) and hands off to `/goalkeeper:goal-prep` for user-reviewable contract drafting.\n- **DONE** — mission's success condition is satisfied; writes `.claude/mission-completed.md` with evidence per success-condition item.\n- **ESCALATE** — supervisor can't decide; surfaces the ambiguity to the user with concrete required input.\n\nMissions are NOT chains. Chains commit to a pre-determined linear sequence; missions adaptively decide the next goal based on prior outputs. Pick the right primitive: chains for sequenceable work known up front, missions for arcs where the shape of step N+1 depends on what step N actually produced.\n\n## State and storage\n\n```\n.claude/\n  mission.md                     # user-authored charter (v0.2, opt-in)\n  mission.json                   # supervisor state\n  mission-log.md                 # append-only mission-level audit trail\n  mission-completed.md           # final snapshot on supervisor DONE\n  goals/\n    active.json                  # pointer to current slug (or terminal record)\n    chain.json                   # current chain, if any\n    .gitignore                   # ignores all goal dirs except shared/ (opt-in)\n    <slug>/\n      contract.md                # the spec\n      state.json                 # status, rejection_count, git baseline, validator/judge results\n      log.md                     # append-only checkpoint + verdict log\n    _archive/                    # cleared goals (moved, never deleted)\n    shared/                      # opt-in committed contracts for team sharing\n```\n\nBy default everything under `.claude/goals/` is gitignored. To share a contract with your team, place it under `.claude/goals/shared/<slug>/contract.md`. The mission files (`mission.md`/`mission.json`/`mission-log.md`) live at `.claude/` root and are NOT auto-gitignored — author your mission charter the way you want it tracked.\n\n### The gk CLI — mechanism lives in code, not prose (v0.4)\n\nEvery state transition — activate, checkpoint, validate, judge verdict, chain advance, pause, resume, clear, and (v0.5) mission init/verdicts — is executed by [`scripts/gk.py`](./scripts/gk.py), a dependency-free Python CLI. The skills carry judgment (what to do next, what to ask the user, what to put in a prompt); gk carries mechanism (every write to the files below). This is what makes the shapes drift-proof: there is exactly one implementation, exercised end-to-end by [`scripts/test-gk.py`](./scripts/test-gk.py).\n\nTwo consequences worth knowing as a user:\n\n- **The hard rules are enforced, not requested.** A PreToolUse hook ([`hooks/hooks.json`](./hooks/hooks.json)) blocks the model from directly editing an active goal's `contract.md`, `log.md`, or `state.json` — contract immutability and the append-only audit trail are mechanical guarantees. The hook fails open: with no active goal it allows everything.\n- **Recovery is a command.** If a session dies mid-chain-advance, `gk doctor --fix` detects and repairs the inconsistency (stale pointers, missing state, un-advanced cursors). You can also run `gk status` yourself in any terminal for an instant read on the active goal.\n\n### Canonical state shapes\n\nAll skills read and write the same shapes via gk. Single source of truth is `scripts/gk.py` (asserted by both test suites); the reference here is for users.\n\n**`active.json`** — exactly two shapes, active or terminal:\n\n```json\n// Active\n{\"slug\": \"<slug>\", \"activated_at\": \"<ISO>\", \"chain\": \"<chain>\"}  // chain is optional\n\n// Terminal\n{\n  \"slug\": null,\n  \"ended_at\": \"<ISO>\",\n  \"ended_reason\": \"done\" | \"cleared\" | \"chain_completed\" | \"aborted\",\n  \"previous_slug\": \"<slug>\",\n  \"previous_chain\": \"<chain>\"\n}\n```\n\n**`<slug>/state.json`** — required fields on activation: `status`, `rejection_count`, `started_at`, `started_at_commit`, `started_at_dirty_paths`. Optional baseline-capture fields: `validator_baseline_result` (pass/fail/not_runnable/null) and `validator_baseline_failing_paths` (paths the validator was already failing on at activation — the judge subtracts these so pre-existing debt doesn't block goal approval). Populated as the goal runs: `chain_step`, `last_checkpoint_at`, `last_validator_result`, `last_judge_verdict`, `last_judge_mode`, `approved_at`, `paused_at`, `resumed_at`, `needs_human_at`, `needs` (what a person must provide, while parked). Goals activated at `provenance_version` 1 also carry `executor` (best-effort caller observables recorded at activation) and `judge_verdicts` — an append-only list of every accepted verdict with its executed judge mode and consumed token id. `gk judge-brief` mints a single-use `<slug>/judge-token.json` (hook-guarded) stamping the mode actually run; `gk verdict` consumes it and refuses caller-typed provenance, and an inline-minted token cannot approve a contract whose `judge_mode` is `subagent`. Goals activated before provenance existed complete under their activation-time rules.\n\n**`chain.json`** — `name`, `slugs[]`, `cursor`, `status`, `started_at`, `completed_at`, `source_file`, `link_approvals[]`. The `link_approvals` array accumulates `{slug, approved_at}` entries as each link is judge-approved, providing chain-level visibility independent of per-link state files.\n\n### The goal directory is private\n\nThe shapes above are documented so you can read them, not so another program can. **They are goalkeeper's working files, not an integration surface** — v0.8.0's park-and-continue changed `active.json` in a way that silently broke an external monitor built on reading them, and that monitor then reported everything fine for 1h46m while a goal sat waiting for a person. A file layout that other components reverse-engineer will do that again.\n\nTwo supported ways to ask goalkeeper what is true:\n\n- **`gk status --json`** — one derived model, the same one `gk status` renders for a human. It carries the active goal, its contract-relative rejection budget, the parked queue with each goal's `needs` text, a top-level `needs_human` flag, and the four raw files under their original keys. Nothing the text output shows is missing from the JSON.\n- **Kernel events** — see below.\n\n### Kernel emission (optional)\n\nIf a shared-kernel emission adapter is present (a module exposing `emitter()`, `Source`, and `Freshness` — discovered at call time, never imported as a dependency), gk announces every state change to it: an append-only audit event on activate, checkpoint, validate, judge verdict, chain start/advance/complete/abort, pause, park, resume, clear, doctor repair, and every mission transition. Each event's payload carries what an outside reader used to dig out of the goal directory — status, rejection count, last judge verdict, chain position, and the full verdict receipt on a judged event. Deleting goalkeeper leaves that history intact.\n\nWhen a goal parks (or a mission escalates), gk additionally raises **one live condition** naming the person who must act, and stands it down when the goal is resumed or cleared. That is the thing a human actually has to see, and the thing a file-reading monitor could not learn.\n\n**This is strictly best-effort and can never affect a goal.** gk runs in fresh clones, worktrees, and machines that have no kernel at all. No adapter found → silent no-op. Adapter or kernel broken → one line on stderr and gk carries on: never an exception, never a non-zero exit, never a refused transition. The end-to-end suite proves it by running the same lifecycle with the kernel absent, un-importable, unavailable, and corrupt, and diffing every gk-owned byte and every line of output against a run with emission off.\n\n| Variable | Effect |\n| --- | --- |\n| `OSD_KERNEL_ADAPTER` | Path to the emission adapter (or its directory). Unset: a short list of documented default paths is tried, then gk goes quiet. |\n| `GK_KERNEL_EMIT=0` | Turn emission off entirely. |\n| `GK_DECISION_OWNER` | Actor reference a raised condition is routed to. Default `actor:chef`. |\n| `GK_KERNEL_DOMAIN` | Kernel domain id. Default `kernel:shared-control`. |\n| `GK_KERNEL_ACTOR` | Overrides the emitting actor reference (default `process:goalkeeper`). Distinct from `GK_ACTOR`, which names the executing agent in `state.json`. |\n\n## Design notes\n\n- **The contract is the spec.** Bad contracts produce bad work. `/goal-prep` is mandatory because thin contracts are goalkeeper's #1 failure mode.\n- **Judge ≠ validator.** Validators check that things work; the judge checks that the *right* things work. Validator passing is necessary but not sufficient.\n- **Subagent judge is the gate.** Independent context catches the placeholders and shortcuts the executing agent rationalized away. Inline judge mode exists for cheap advisory review only — do not use it as a gate.\n- **Append-only log.** Logs are forensic artifacts. Past entries are never deleted or rewritten.\n- **Skills decide, gk writes.** (v0.4) Prose-as-runtime was goalkeeper's original architecture and its biggest liability: every invariant depended on the model faithfully hand-executing a state machine described across five skill files. Moving the mechanics into one tested CLI makes verdicts reproducible (judge briefs are assembled byte-for-byte identically), makes interruptions recoverable (`gk doctor`), and turns the hard rules into hook-enforced guarantees.\n- **Wakeup pacing defers to the harness.** Set `wakeup_seconds` in the contract to pin a delay; otherwise the model follows the harness's live ScheduleWakeup guidance rather than goalkeeper second-guessing prompt-cache behavior that changes across versions.\n- **Anti-placeholder.** Borrowed verbatim from Ralph: stubs, mocks, `.todo`, `.skip`, and \"TODO: real implementation\" are automatic judge rejection.\n\n## Prior art\n\n- **OpenAI Codex `/goal`** — the durable-objective + stop-condition pattern. goalkeeper mirrors the lifecycle (set, status, pause, resume, clear) and adds prep, judge, and chain. ([docs](https://developers.openai.com/codex/use-cases/follow-goals))\n- **Ralph (Geoffrey Huntley)** — the embedded-validator loop. goalkeeper adopts the validator philosophy and the anti-placeholder rule, and adds an external judge. ([blog post](https://ghuntley.com/ralph/))\n\n## License\n\nMIT — see [LICENSE](./LICENSE).\n\n## Brand\n\nBrand assets (logo, wordmark, social card) live in [`branding/`](./branding/) — see the [brand README](./branding/README.md) for usage notes, color palette, and SVG→PNG conversion. Drafts and alternates are intentionally outside this repo.\n\n---\n\n<div align=\"center\">\n  <img src=\"branding/mark.svg\" alt=\"goalkeeper\" width=\"48\">\n</div>\n",
  "bytes": 24762,
  "sha": "4e9291cdf866ddc67cb21c7741cdeb89628c6acbdf49bc97b09b2e942f294664",
  "repo_slug": "itsuzef/goalkeeper",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/plg_itsuzef_goalkeeper_goalkeeper_de82f3d2/readme"
}