# effect-gh > An Effect v4 SDK for the GitHub CLI. # Overview Source: https://effect-gh.timmo.dev/ effect-gh wraps the GitHub CLI (`gh`) in Effect v4 services. Each operation runs `gh` without a shell, decodes its output through Schema and fails with a typed error, so you can compose GitHub work with the rest of your Effect program. ```ts import { NodeServices } from "@effect/platform-node"; import { Effect, Layer } from "effect"; import { Issue, layer } from "@timmo001/effect-gh"; const program = Issue.list({ repo: "owner/repo", state: "open", limit: 10 }); Effect.runPromise( program.pipe(Effect.provide(layer().pipe(Layer.provide(NodeServices.layer)))), ); ``` ## Get started 1. **Install** Add the package and the Effect packages it needs. See [Install](/install). 2. **Provide the layer** Choose your platform layer and build the `Gh` layer from it. See [Core SDK](/core). 3. **Call an operation** Use the typed operations for repositories, issues, pull requests, workflows and more. See [GitHub operations](/github). ## Reference **[Core SDK](/core)** The `Gh` service, options, errors and cancellation. **[API requests](/api)** REST and GraphQL requests, pagination and retries. **[GitHub operations](/github)** The typed operations by area. **[Agents](/agents)** The agent skill and the OpenCode plugin. **[Development](/development)** Checks, tooling drift and releases. ## Machine-readable docs | URL | Use | | ----------------------------------------- | ---------------------------------------- | | https://effect-gh.timmo.dev/llms.txt | Compact page index | | https://effect-gh.timmo.dev/llms-full.txt | Full docs bundle | | https://effect-gh.timmo.dev/mcp | Hosted MCP (search, page and navigation) | | `/{route}.md` | One page as Markdown | --- # Agents Source: https://effect-gh.timmo.dev/agents ## Skill [The effect-gh skill](https://github.com/timmo001/effect-gh/blob/main/skills/effect-gh/SKILL.md) guides Effect integrations towards the package and its current documentation. It follows the [Agent Skills specification](https://agentskills.io/specification). ```sh npx skills add timmo001/effect-gh ``` Or import the complete `skills/effect-gh/` directory unchanged into your skill collection. ## OpenCode plugin `opencode-plugin/` ships the SDK as OpenCode's `gh` Code Mode namespace, so an agent can call typed GitHub tools instead of the `gh` CLI. ## Docs for agents These docs are served as `llms.txt`, `llms-full.txt` and a read-only MCP endpoint at `https://effect-gh.timmo.dev/mcp`. See the [overview](/#machine-readable-docs). --- # API requests Source: https://effect-gh.timmo.dev/api ## REST `Api.json(request, schema)` decodes one JSON response. `Api.empty(request)` runs a request whose body isn't needed, such as a 204 mutation. `Api.text(request)` returns the body as text, and `Api.headers(request)` returns the response headers with lower-cased names. `Api.raw(request)` returns the buffered output and exit code. Every request names its method. ```ts import { Api } from "@timmo001/effect-gh"; import { Schema } from "effect"; const viewer = Api.json( { endpoint: "user", method: "GET" }, Schema.Struct({ login: Schema.String }), ); ``` Requests accept `hostname`, `headers`, `query`, a JSON `body` and core overrides under `options`. JSON bodies go through stdin rather than CLI field interpolation. `Api.pages(request, pageSchema)` runs a bodyless GET with `--paginate --slurp` and returns an array of decoded pages, not flattened items. `Api.conditional(request, schema, validators?)` sends the `etag` and `lastModified` from an earlier response and returns `Modified` or `NotModified`. A 304 costs no primary rate limit. ## GraphQL `Graphql.query(document, variables, schema, options?)` posts through `gh api graphql` and decodes `data`. A response with `errors` fails with `GraphqlResponseError`. `Graphql.operations(document)` parses a document without running it and returns each operation's kind, name and top-level fields, so you can tell reads from writes. ## Retries No operation retries automatically, because a mutation may have taken effect even when the CLI times out. `Gh.retryTransient(options?)` retries known-transient failures: timeouts, rate limits, HTTP 408, 429 and 5xx statuses, and network failures. It defaults to three retries with jittered exponential backoff. ```ts import { Api, Gh } from "@timmo001/effect-gh"; import { Schema } from "effect"; const viewer = Api.json( { endpoint: "user", method: "GET" }, Schema.Struct({ login: Schema.String }), ).pipe(Gh.retryTransient({ times: 2 })); ``` ## Rate limits `RateLimit.get(resource?, options?)` reads the `core`, `graphql` or `search` quota, which doesn't count against it. `RateLimit.cached` caches those reads and `RateLimit.guard` waits for the reset, or fails with `RateLimitExhausted`, once the remaining requests run low. --- # Core SDK Source: https://effect-gh.timmo.dev/core `Gh` is the service tag. `layer(options?)` captures the consumer-provided `ChildProcessSpawner` and starts no processes until an operation runs. You choose the platform layer and the runtime. ```ts import { NodeServices } from "@effect/platform-node"; import { Effect, Layer, Schema } from "effect"; import { Gh, layer } from "@timmo001/effect-gh"; const ghLayer = layer({ timeout: "30 seconds" }).pipe( Layer.provide(NodeServices.layer), ); const program = Effect.gen(function* () { const gh = yield* Gh; return yield* gh.json( ["api", "user"], Schema.Struct({ login: Schema.String }), ); }).pipe(Effect.provide(ghLayer)); ``` ## Raw methods - `execute(args, options?)` returns `{ stdout, stderr, exitCode }` on exit zero, buffering both outputs in memory. - `json(args, schema, options?)` decodes stdout as JSON through the Schema you supply. - `stream(args, options?)` emits `{ _tag: "Stdout" | "Stderr", text }` chunks with backpressure. Chunk boundaries are not line boundaries. - `interactive(args, options?)` runs on the terminal's stdin, stdout and stderr, so gh and git can show progress and ask for credentials. Prefer the typed operations. Reach for the raw methods only for a command the SDK doesn't cover yet. ## Options `GhOptions` accepts `executable` (default `gh`), `cwd`, `env`, `stdin`, `timeout` and `maxOutputBytes`. Call options override layer defaults, and environment entries merge. There is no timeout by default, and `timeout: null` disables a layer timeout. Commands pass literal argv without a shell. The SDK always disables prompts, colour, forced TTY and spinners, and selects `cat` as the pager. Stdin is closed unless you supply it. No operation retries automatically. ## Errors Errors are tagged: | Error | When | | ----------------------- | ----------------------------------------------- | | `GhCommandError` | Nonzero exit, with exit code, stdout and stderr | | `GhPlatformError` | Spawn or pipe failure | | `GhTimeoutError` | The timeout passed | | `GhOutputLimitError` | Stdout passed `maxOutputBytes` | | `GhDecodeError` | Invalid JSON or a Schema mismatch | | `GhInvalidOptionsError` | Operation options rejected before spawning | Command errors keep the last 65,536 UTF-16 code units of each output and flag truncation with `stdoutTruncated` and `stderrTruncated`. ## Cancellation Interruption, timeout and early stream termination close the child scope. The platform terminates the child, escalating after one second if needed. Interruptions stay Effect interruptions rather than becoming SDK errors. --- # Development Source: https://effect-gh.timmo.dev/development Use the tool versions pinned in `mise.toml` and Bun for dependencies. ```sh mise run check ::: build ``` ## Docs ```sh mise run docs:dev mise run docs:check ``` `docs:dev` runs the dev server, and `docs:check` checks the pages, links and types in an isolated folder so it doesn't clash with a running dev server. Run `bun run brand` in `docs` to regenerate the logo PNGs and the GitHub social preview from `src/assets/logo.svg`. ## Tooling drift `tooling.yml` records the gh release the SDK was last reviewed against, and `tooling/gh` holds a snapshot of the help and `--json` fields of every wrapped command at that release. Upkeep's [`drift` job](https://upkeep.timmo.dev/running#drift) flags when a newer [cli/cli](https://github.com/cli/cli/releases) release appears. To review a new release, run `mise run tooling:refresh `, review the diff against the wrappers in `src/`, then set `reviewed` in `tooling.yml` and push. Add a command to the list in `scripts/gh-contract.ts` when the SDK starts wrapping it. ## Releases Run `mise exec -- npm pack --dry-run` to inspect the package locally. For a release, set the package version, keep `bun.lock` in sync and pass `mise run check` and `mise run build`. Commit and push the reviewed version, then publish a GitHub release with a tag matching the version exactly. The release workflow validates and builds the package, then publishes to npm with provenance. --- # GitHub operations Source: https://effect-gh.timmo.dev/github Each operation takes an optional `repo` (or `repository`) and core execution options last. Selectors are validated before spawning and fail with `GhInvalidOptionsError`. | Module | Covers | | ---------------------------------------------- | ------------------------------------------------------------------------ | | `Repository` | View, create, edit, fork, clone, read files and directories | | `Issue`, `IssueArtifact` | List, query, create, edit, comment, close, link branches and artifacts | | `PullRequest` | List, query, create, edit, merge, checks, diff, checkout and status | | `Workflow` | Runs, jobs, logs, watch, dispatch, rerun and the workflows themselves | | `Cache`, `Secret`, `Variable` | Actions caches, secrets and variables | | `Review`, `Comment`, `Discussion` | Review threads, submitted reviews, conversation comments and discussions | | `Attachment` | Upload an image or video for use in a post | | `Auth`, `Config`, `Alias` | Authentication, gh settings and aliases | | `Release`, `Attestation`, `Label`, `Search` | Releases and their verification, labels and search | | `Project`, `Codespace` | Projects and codespaces | | `Gist`, `SshKey`, `GpgKey`, `DeployKey`, `Org` | Gists, keys and organisations | | `Extension`, `Skill`, `AgentTask`, `Cli` | Extensions, skills, agent tasks and the gh version | ```ts import { Issue, PullRequest, Repository } from "@timmo001/effect-gh"; const repository = Repository.view("owner/repo"); const issues = Issue.list({ repo: "owner/repo", state: "open", limit: 50 }); const checks = PullRequest.checks(42, { repository: "owner/repo", required: true, }); ``` Mutations never retry. See the [README](https://github.com/timmo001/effect-gh#readme) for the details of each module. ## Commands left out on purpose These gh commands only make sense on a terminal or in a browser, so the SDK doesn't wrap them: `browse`, `completion`, `copilot`, `licenses`, `preview`, `extension browse`, the interactive `auth login`, `codespace ssh`, `code`, `jupyter` and `ports forward`. Use `Gh.interactive` to run one on the terminal. --- # Install Source: https://effect-gh.timmo.dev/install ## Requirements - Effect matching `peerDependencies.effect` in the package's `package.json`, with a compatible platform adapter of your choosing. - GitHub CLI 2.97.0 or later, installed and authenticated through `gh auth login` or its standard token environment variables. - An ESM consumer. Bun runs the TypeScript source directly; Node uses the compiled JavaScript in a built package. ## Bun Run from your application directory: ```sh bun add github:timmo001/effect-gh bun add "effect@$(bun -p 'require("@timmo001/effect-gh/package.json").peerDependencies.effect')" \ "@effect/platform-node@$(bun -p 'require("@timmo001/effect-gh/package.json").devDependencies["@effect/platform-node"]')" ``` Commit `package.json` and `bun.lock` to keep the resolved GitHub revision and dependency versions. The `bun` export resolves to `src/index.ts`, so a GitHub install needs no build step. ## Node Build a package from source with the [mise](https://mise.jdx.dev/getting-started.html)-pinned tools: ```sh git clone https://github.com/timmo001/effect-gh.git .effect-gh ( cd .effect-gh && mise install && mise run install && mise exec -- npm pack --pack-destination .. ) bun add ./timmo001-effect-gh-*.tgz ``` Keep the archive with your application for repeatable installs. ## GitHub authentication Install [GitHub CLI](https://cli.github.com/) separately, then authenticate: ```sh gh auth login gh auth status ``` For automation, supply `GH_TOKEN` or another gh-supported configuration through the environment. The SDK uses it without a separate login.