{
  "markdown": "# dude\n\n![Dude Workflow](docs/dude.png)\n\nYet another AI workflow.\n\ndude packages the skills that carry a change from an issue to a pull request\nwhose CI passes and whose review is clean, and the skills that root-cause a\nproblem before any change is proposed. Merging is left to a person, and so is\neverything that depends on it. They are written to wire into one another: each\nnames the next flow rather than absorbing it, and each has its own gate.\n\n## Skills\n\n| Skill                     | What it produces                                                                   |\n| ------------------------- | ---------------------------------------------------------------------------------- |\n| `using-dude`              | The workflow rules the other skills are wired by                                   |\n| `plan-work`               | An agreed design plus a numbered TODO list at PR granularity                       |\n| `implement-work`          | A draft PR on a pushed branch of verified commits, for one PR-sized task           |\n| `pr-to-ready`             | A PR whose CI passes and whose review is clean                                     |\n| `review-plan`             | Findings on a TODO list or an implementation plan                                  |\n| `review-code`             | A diff, branch, or working tree reviewed, with no blocking finding left unresolved |\n| `simplify-code`           | Recently changed code simplified, behavior preserved                               |\n| `investigate-performance` | An evidence-backed explanation of a performance shortfall                          |\n| `investigate-anomaly`     | A blameless findings report on a failure, incident, or drifting metric             |\n\nThe change flow is `plan-work` → `implement-work` → `pr-to-ready`, entered at\nwhichever stage the work has actually reached. An investigation runs first\nwhere the cause is unknown, and hands its findings to `plan-work`.\n\n## Requirements\n\ndude requires [Superpowers](https://github.com/obra/superpowers#installation). Install it first, in the same runtime you are installing dude into. dude's skills name Superpowers procedures and neither ship nor reimplement them, so those calls have nowhere to go until Superpowers is present.\n\n## Install\n\nOpenCode:\n\n```jsonc\n{\n  \"$schema\": \"https://opencode.ai/config.json\",\n  \"plugin\": [\n    \"dude@git+https://github.com/yowcow/dude.git\",\n    \"superpowers@git+https://github.com/obra/superpowers.git\",\n  ],\n}\n```\n\nPut `$schema` and both `plugin` entries in the global\n`~/.config/opencode/opencode.jsonc`, then restart OpenCode.\n\nClaude Code:\n\n```\n/plugin marketplace add yowcow/dude\n/plugin install dude@dude\n```\n\nCodex:\n\n```\ncodex plugin marketplace add yowcow/dude\ncodex plugin add dude@dude\n```\n\nCodex records hook trust per hook rather than per plugin — `~/.codex/config.toml`\ngains a `[hooks.state.\"dude@dude:hooks/hooks.json:session_start:0:0\"]` entry\ncarrying a `trusted_hash`. Installing dude does not grant it: `codex plugin add`\nrecords no such entry, and an untrusted hook does not run, so `using-dude` is\nnot in context until you trust it — though the skills are still invocable\nby name. Codex asks at the start of the next interactive session instead: after\nthe directory-trust prompt, a `Hooks need review` prompt offers to review the\nhook, trust it, or continue without trusting. Codex's hook trust decides whether\ninjection happens at all.\n\n## Versions\n\ndude is not versioned. Every runtime is meant to carry the default branch's\nlatest commit, so there is no release to cut and nothing to bump. Which manifest\ncarries a `version` at all follows from what each runtime does with one:\n\n| Runtime     | Requires `version`?        | Uses it to decide an update?                              |\n| ----------- | -------------------------- | --------------------------------------------------------- |\n| OpenCode    | yes                        | no, git-backed installs do not use it to decide an update |\n| Claude Code | no — `validate` only warns | **yes — a version left in place stops updates**           |\n| Codex       | yes, strict semver         | no                                                        |\n\nSo the two manifests Claude Code reads — `.claude-plugin/plugin.json` and the\nplugin entry in `.claude-plugin/marketplace.json` — carry no `version`.\n`claude plugin update dude@dude` compares commits instead, and the version it\nreports is a short commit sha. An install made while those manifests still said\n`0.1.0` moves onto sha-tracking at its first `claude plugin update`, so nobody\nhas to reinstall. `claude plugin validate .` warns that no version is specified;\nthat warning is the expected state here, not something to fix.\n\n`.codex-plugin/plugin.json` and `package.json` keep `\"version\": \"0.1.0\"`\nbecause their formats require one — and **that value is never bumped**, because\nneither git-backed route reads it to decide an update. Codex installs the\nmarketplace snapshot's root directory itself, with no per-version cache in\nbetween. OpenCode caches the commit first installed for an unchanged git spec; restarting,\nremoving and re-adding the config entry, or rerunning `opencode plugin` with that\nspec does not refresh it. To update to HEAD, quit OpenCode, remove\n`~/.cache/opencode/packages/dude@git+https:/github.com/yowcow/dude.git`, and\nrestart.\n\nWhat each runtime printed when this was measured — and the throwaway plugins the\nversion-less control was taken with — is recorded in\n[issue #42](https://github.com/yowcow/dude/issues/42).\n\n## Use\n\n### A typical run\n\nA run starts with an issue and ends with a pull request waiting for a person to\nmerge it — that issue plus three prompts, across three kinds of session:\n\n1. **Open an issue** for what you want done. It is what the plan gets published\n   against, and the parent the sub-issues hang from.\n\n2. **Plan it, in one session:**\n\n   ```\n   /dude:plan-work <issue-url>\n   ```\n\n   It researches, agrees a design with you, then publishes that design plus a\n   numbered TODO list at PR granularity as one comment on the issue, and opens\n   one sub-issue per item.\n\n3. **Implement each item, one fresh session per sub-issue:**\n\n   ```\n   /dude:implement-work <sub-issue-url>\n   ```\n\n   It takes the item to a draft PR on a pushed branch of verified commits, and\n   ends by naming that PR's URL.\n\n4. **Take that PR to ready, in another fresh session:**\n\n   ```\n   /dude:pr-to-ready <pr-url> (ready-on-clean=yes)\n   ```\n\n   It resolves the branch, base and repository from the PR, sets up its own\n   worktree, then loops on CI and review until both are clean.\n\nThen wait for the PR.\n\nSteps 3 and 4 are separate sessions rather than two prompts in one because the\nsecond flow's cost is dominated by re-reading the first's context: measured over\n126 runs, the stretch after `pr-to-ready` starts is 41% of the session's cost,\nand starting it fresh drops what it re-reads each turn from roughly 269k tokens\nto roughly 60k. Handing it the PR URL is what makes that split practical — the\nreference is the whole entry, so no branch name has to be remembered and no\ncheckout prepared by hand.\n\nA run's sessions need not all be at the same tier. `plan-work` is where a\ndesign gets agreed and a bad call is expensive to undo, so give it the highest\ntier you have; `implement-work` and `pr-to-ready` mostly execute and inquire,\nand a cheaper tier carries their main loops. Three sections below are what that\nleaves you to handle: **What tier a marked worker runs at**, for keeping the\nmarked workers high once the session under them is cheap,\n**What the run's own tier decides**, for the judgments that come down with the\nsession instead of staying with those workers, and **What else the run's cost\nrides on**, for the session length and worker count that remain after the\nmodel is cheap. Effort is not split the same\nway: on Claude Code a worker runs at the session's effort, so launch even a cheap\nsession at the effort you want its marked workers to have. Whether a cheap main loop still\nruns `implement-work`'s gates and `pr-to-ready`'s clean judgment at the same\nfidelity is not settled here, so nothing below reports that it holds. That\nunsettled question does not reach **What the run's own tier decides**, which\nrecords a different thing: where those judgments run, and what a lowered tier\ncosts them.\n\nOnly the first name on each of those lines is a slash command: Claude Code\npasses everything after it to the skill as free text. That is what makes\n`ready-on-clean=yes` usable — it answers in the same breath the one question\n`pr-to-ready` would otherwise stop and ask, whether a clean run should mark the\nPR ready. It is not a parsed flag; the argument text is read rather than matched\nagainst a grammar, and it is Claude Code's pass-through that was measured, not\nthe other runtimes'.\n\nA fresh session per sub-issue is the shape rather than a preference: a later PR\nis never a continuation of the previous one and inherits none of its\nverification. And dude stops at ready — merging, closing the parent issue, and\ndeleting the branch and worktree are yours.\n\n### How each runtime reaches the skills\n\n| Runtime     | `using-dude` in context at session start?                              | How to reach it by hand |\n| ----------- | ---------------------------------------------------------------------- | ----------------------- |\n| OpenCode    | yes — `experimental.chat.messages.transform` on the first user message | —                       |\n| Claude Code | yes — a SessionStart hook                                              | —                       |\n| Codex       | yes, once the hook is trusted                                          | `dude:using-dude`       |\n\nEach row's evidence is in the prose below.\n\nOpenCode's package plugin registers all nine skills and prepends `using-dude`\nto the first user message through `experimental.chat.messages.transform`, so\nit is in context at session start. The injected text uses a dude-only marker\nand does not contain `EXTREMELY_IMPORTANT`, so Superpowers' bootstrap and\nthis one do not skip each other. Two loads of the same plugin (a global git\ninstall plus the checkout's `.opencode/plugins/`) still inject once.\n\nClaude Code needs no invocation: a SessionStart hook puts `using-dude` in context\nat the start of every session.\n\nCodex installs all nine skills and runs `hooks/hooks.json` once the hook is\ntrusted, so `using-dude` is in context there too — the Install section above\ncovers what trust involves. The `dude:using-dude` skill works whether the hook is\ntrusted or not.\n\nThe skill bodies use bare names (`plan-work`), because the `dude:` prefix is a\nplugin namespace the host adds.\n\n### What tier a marked worker runs at\n\n`using-dude`'s **Worker tier** sends a worker whose miss would pass as \"nothing\nfound\" out at the highest tier the runtime can put on a worker, which the run's\nown tier does not cap. Which tier that actually is gets\nsettled by a ladder rather than by the run alone: where the runtime carries a\ndefault for subagents, a worker dispatched without a model of its own lands on\nthat default; where none is set, it falls back to the run's own tier. So raising\nthe run's own tier does not reach the marked worker on a machine whose subagent\ndefault sits below it — the mark reads as satisfied while that worker runs a\ntier under the session that dispatched it.\n\nMeasured on Claude Code 2.1.258, the two rungs have separate carriers. The\nsubagent default is the environment variable `CLAUDE_CODE_SUBAGENT_MODEL`; the\nrun's own model is the `model` key in `settings.json`, or `claude --model` at\nlaunch. The first is carried by no settings key of its own, so the check goes to\nthe environment — the session's own rather than a bare login shell, since a\nsettings file's `env` block is documented as carrying variables too:\n\n```\nenv | grep CLAUDE_CODE_SUBAGENT_MODEL\n```\n\nOutput naming a model below the highest tier you have is the case where the mark\nquietly loses, and this repository's own machine was measured in exactly that\nstate: `sonnet` there, exported from a shell profile, while `settings.json`\ncarried no subagent key whatsoever. Reading `settings.json`\nalone is what makes such a default look absent.\n\nWhat the dispatched worker then ran on has to be read back from its transcript\nrather than asked of it — a self-report is the worker's own account rather than\nthe runtime's record of the call, and the environment it inherits can name a\nmodel it is not running. Read that way here, a worker dispatched with no model\nnamed ran on `claude-sonnet-5` while the session ran `claude-opus-5`; one\ndispatched with `opus` named ran on `claude-opus-5`. Empty output from the check\nmeans no default is set, and the fallback to the parent's model is then\ndocumented behavior rather than something this repository has observed.\n\nClose the gap at the default: set `CLAUDE_CODE_SUBAGENT_MODEL` to the highest\ntier you have and leave it there whatever tier the run itself is on — a session\nmeasured at a cheap tier dispatched workers a tier above itself throughout.\nSetting it there is what makes the mark independent of the dispatch, and\ndispatches do omit the model in practice: 5 of the pilot's 55 workers were\ndispatched with none named, and in the one window measured alone that was 2 of\n4, a marked reviewer among them\n([measurements](https://github.com/yowcow/dude/issues/169#issuecomment-5535611330)).\nIt also reaches a worker that a worker dispatched, two levels down, which the\ndispatching skill never sees. Naming the model at the dispatch was measured\nworking too, and remains right to do; it just has to be got right once per\ndispatch, where the default is got right once.\n\nThe default knows nothing of which workers are marked, so every worker\ndispatched without a model of its own rises with it, the unmarked ones and their\ncost included, while the session stays where it is. A fork stays outside its\nreach: it continues the parent's context and runs the parent's model by\nconstruction, so a cheap session forks cheap however the default is set. It can\ncost more per request than the session it came from, having inherited the\ncontext at its largest\n([measurements](https://github.com/yowcow/dude/issues/169#issuecomment-5535611330)).\ndude's skills dispatch no forks, so this is a mechanism to recognize rather than\na hole in the flow.\n\nOn Claude Code, reasoning effort needs no ladder, because a worker runs at the session's effort.\nEvery worker across the pilot's four windows did, including workers running a\nmodel the session was not — which is what rules out their having picked it up\nfrom a per-model setting\n([measurements](https://github.com/yowcow/dude/issues/169#issuecomment-5534943995)).\nThere is no lever here for spending less on the work you would rather run\ncheaply, and a per-agent definition is not one: its `effort:` binds only where\na dispatch names that agent type, dude's dispatches do not, and the only route\nleft would be writing dude's own wiring into your instruction file — which\nyowcow/dude#139 declined.\n\nPrompt-cache TTL is a third setting, and it splits the main loop from its\nworkers by construction. Claude Code carries it as `promptCacheTtl` in\n`settings.json`, whose own description scopes it to the main conversation, and\nas `CLAUDE_CODE_PROMPT_CACHE_TTL` in the environment, which takes precedence;\nboth take `5m` or `1h`, and a `subagentPromptCacheTtl` sits beside it for\neverything outside the main conversation. Measurement agrees with that scope:\nthe window set to `5m` wrote all of its main-loop cache at `5m`, while subagents\nwrote at `5m` in every window — the pilot varied only the main-conversation\nsetting, so that is the reach of the finding rather than a property of\nsubagents. That it can be chosen at all corrects yowcow/dude#159's record of\nit as fixed. Which way it moves cost is not settled — the cheaper write is\noffset by having to write more often, and separating the two needs the same work\nreplayed\n([measurements](https://github.com/yowcow/dude/issues/169#issuecomment-5534943995)).\n\nAll three were measured on Claude Code alone; what the other two runtimes do\nwith a subagent default, a session's effort, or a cache TTL has not been\nmeasured here, which is not a claim that they have no tier lever. Where a\nruntime turns out to have none, `using-dude`'s **Worker tier** leaves it\nundecided by design — so leave the main loop's tier alone there, because a\nmarked worker with no rung above the session has nowhere to stand once the\nsession comes down.\n\n### What the run's own tier decides\n\nThat ladder is about workers, and a review finding's verdict is on that side of\nit. `implement-work` owns both of its gates and lets no worker declare either\nclean. `pr-to-ready` never delegates the clean judgment or the stop\nconditions, reading whether checks pass included. `simplify-code` dispatches\nproposers and keeps in the main loop which of their proposals are accepted;\n`review-code` and `review-plan` dispatch reviewers that return findings, and\nthere the main loop is what decides when the loop or the pass ends. Each of\nthose skills draws that line in its **Orchestration model**, and the three that\njudge a review finding put that one judgment on the far side alike: in\n`review-code`, `review-plan` and `pr-to-ready`, a finding's verdict goes out to\na marked worker and never runs in the main loop.\n\nSo the run's own tier is not only the fallback a worker dispatched without a\nmodel of its own lands on. Lower it to spend less and every judgment on the\nnear side of those lines goes down at once, while the run keeps reporting\n_clean_ — what fell is the judgment rather than the shape of the output, so no\ngate in the flow has anything to catch. Where that fallback is the one in\nforce, the far side comes down with it.\n\n### What else the run's cost rides on\n\nThe same cheap main loop, measured in two windows, cost 2.4× per request\n($0.1091 against $0.0452) once the session had grown: `cache_read` / request\n372,916 against 153,951, over 3 hours 15 minutes against 22 minutes. The cheap-tier\nsaving is eaten by that growth; window 1 came back 5.7% under the lower end of\nthe band in yowcow/dude#161. In that longer window the workers carried 73.3% of\nthe $60.92 total. A worker request was cheaper than the main loop's ($0.0583\nagainst $0.1091); the count is what dominated. Lowering the main loop's tier\ndoes not reduce that share\n([measurements](https://github.com/yowcow/dude/issues/169#issuecomment-5534943995);\n[correction](https://github.com/yowcow/dude/issues/169#issuecomment-5535611330)).\n\n## Development\n\nPoint a marketplace at a local clone instead of the remote:\n\n```\n/plugin marketplace add ~/repos/dude\n/plugin install dude@dude\n\ncodex plugin marketplace add ~/repos/dude\ncodex plugin add dude@dude\n```\n\nStarting OpenCode from the repository checkout loads\n`.opencode/plugins/dude.js` as a project plugin. Temporarily remove any globally\nconfigured dude plugin entry first, then use the native `skill` tool to verify\nall nine local skills.\n\nInstalling dude a second time under a throwaway name is not a way to try hook\nchanges out. Two installs run the `SessionStart` hook twice, and both blocks\nreach the same session. Which tree each one came from is readable — the injected\ntext names the install path it ran from — but the session is still carrying\n`using-dude` twice, counted twice against the context and in two versions that\ndisagree wherever the branch has moved. Read the rules from the older block and\nthe session was not checking the branch at all. Uninstalling the github-sourced\n`dude@dude` first is what avoids that, and it rewrites somebody's plugin\nenvironment — ask whoever owns it.\n\nThe hazard is the hook running twice, so it is not specific to Claude Code:\nCodex runs `hooks/hooks.json` too, once the hook is trusted. Whether a second\nCodex install injects twice as well has not been measured here.\n\nCheck the manifests before installing — the validators name the offending\nfield:\n\n```\nclaude plugin validate .\npython3 ~/.codex/skills/.system/plugin-creator/scripts/validate_plugin.py .\npython3 -m json.tool .agents/plugins/marketplace.json >/dev/null\n```\n\n`claude plugin validate` starts from `.claude-plugin/marketplace.json` and reaches that same\n`plugin.json` through the entry's `\"source\": \"./\"`, which is where its `No version\nspecified` warning comes from — expected here, per the Versions section above.\nThe Codex validator reads `.codex-plugin/plugin.json` and walks every `SKILL.md`\nas well, so it catches malformed frontmatter at the same time. Neither manifest\nvalidator named above looks at\n`.agents/plugins/marketplace.json` — each still passes with that file\ndeliberately corrupted — so `python3 -m json.tool` is what covers it, syntax\nonly. `make lint test` checks none of them: it covers shell and the test suite.\n\n`AUTHORING.md` holds the rules for writing and editing these skills — where\neach kind of text belongs, and the deletion test every sentence has to pass.\n\n## License\n\nMIT. See `LICENSE`.\n",
  "bytes": 21066,
  "sha": "957fcee9fa7f7e17bfafa92dae398da5d88cd8a3bac4eb2da3e0dac0c9a35a24",
  "repo_slug": "yowcow/dude",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/plg_yowcow_dude_675efc61/readme"
}