@agent-relay/github-primitive 4.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # @agent-relay/github-primitive
2
+
3
+ GitHub workflow primitive for Agent Relay. It exposes a typed client and a
4
+ workflow integration step that can run through the local `gh` CLI or a cloud
5
+ GitHub proxy.
6
+
7
+ ## Runtime Selection
8
+
9
+ The primitive supports three runtime modes:
10
+
11
+ - `auto`: prefer cloud when Nango or relay-cloud credentials are present,
12
+ otherwise use the local `gh` CLI when available.
13
+ - `local`: use `gh api` from the current machine.
14
+ - `cloud`: use Nango first, then relay-cloud proxy when configured.
15
+
16
+ ```ts
17
+ import { GitHubClient } from '@agent-relay/github-primitive';
18
+
19
+ const github = await GitHubClient.create({
20
+ runtime: 'auto',
21
+ });
22
+
23
+ const repo = await github.getRepo('AgentWorkforce', 'relay');
24
+ console.log(repo.defaultBranch);
25
+ ```
26
+
27
+ ## Actions
28
+
29
+ The client and workflow step support:
30
+
31
+ - Repositories: `listRepos`, `getRepo`
32
+ - Issues: `listIssues`, `createIssue`, `updateIssue`, `closeIssue`
33
+ - Pull requests: `listPRs`, `getPR`, `createPR`, `updatePR`, `mergePR`
34
+ - Files: `listFiles`, `readFile`, `createFile`, `updateFile`, `deleteFile`
35
+ - Branches and commits: `listBranches`, `createBranch`, `listCommits`, `createCommit`
36
+ - Identity: `getUser`, `listOrganizations`
37
+
38
+ ## Workflow Step
39
+
40
+ ```ts
41
+ import { createGitHubStep } from '@agent-relay/github-primitive/workflow-step';
42
+
43
+ createGitHubStep({
44
+ name: 'read-readme',
45
+ action: 'readFile',
46
+ repo: 'AgentWorkforce/relay',
47
+ params: {
48
+ path: 'README.md',
49
+ },
50
+ output: {
51
+ mode: 'data',
52
+ format: 'text',
53
+ },
54
+ });
55
+ ```
56
+
57
+ See `examples/github-step.ts` for a workflow runner example and
58
+ `examples/github-client.ts` for a standalone client example.
59
+
60
+ ## End-to-end PR workflow
61
+
62
+ `examples/end-to-end-pr-workflow.ts` walks the full PR lifecycle against
63
+ a single runtime:
64
+
65
+ 1. `getRepo` — inspect + log the selected runtime.
66
+ 2. `createBranch` off the base branch.
67
+ 3. `createFile` — write a marker file on the new branch.
68
+ 4. `createPR` — the core integration step cloud workflows migrate to.
69
+ 5. `getPR` — round-trip verify.
70
+ 6. `updatePR` — edit the PR body without code changes.
71
+ 7. `listPRs` — confirm indexability.
72
+ 8. (commented) `mergePR` — off by default so the example never merges
73
+ anything real.
74
+
75
+ Run against a scratch repo:
76
+
77
+ ```
78
+ GITHUB_REPO=AgentWorkforce/scratch npx tsx \
79
+ packages/github-primitive/examples/end-to-end-pr-workflow.ts
80
+ ```
81
+
82
+ Runtime selection is automatic:
83
+
84
+ | Path | Triggered when |
85
+ | ----------- | ----------------------------------------------------------------------------------------------------- |
86
+ | Local `gh` | `gh auth status` succeeds and no cloud creds are set |
87
+ | Nango | `NANGO_SECRET_KEY` + `NANGO_GITHUB_CONNECTION_ID` + `NANGO_GITHUB_PROVIDER_CONFIG_KEY` present |
88
+ | relay-cloud | `RELAY_CLOUD_API_URL` + `RELAY_CLOUD_API_TOKEN` + `WORKSPACE_ID` present (fallback when Nango absent) |
89
+
90
+ ## Multi-tenant routing
91
+
92
+ In cloud, every workspace has its own GitHub App install — one Nango
93
+ connection per tenant. `createGitHubStep` accepts a per-step `config`
94
+ field so a single workflow can route different actions through
95
+ different connections. One workflow, many tenants.
96
+
97
+ `examples/multi-tenant-pr-workflow.ts` demonstrates this: it opens PRs
98
+ in `AgentWorkforce/cloud` (via the AgentWorkforce app) AND in an
99
+ MSD-owned repo (via MSD's app), from the same workflow definition, by
100
+ varying the `config:` field on each step.
101
+
102
+ ### Cloud adoption: the resolver helper
103
+
104
+ The primitive stays tenant-unaware — it takes a `GitHubRuntimeConfig`
105
+ and does what it's told. The tenant lookup lives in cloud, in a small
106
+ helper:
107
+
108
+ ```ts
109
+ // cloud/packages/web/lib/github/connection-resolver.ts
110
+ import type { GitHubRuntimeConfig } from '@agent-relay/github-primitive';
111
+
112
+ export async function githubConfigForRepo(opts: {
113
+ repo: string; // "owner/repo"
114
+ workspaceId?: string; // optional — cloud-owned repos default
115
+ }): Promise<GitHubRuntimeConfig> {
116
+ const connection = await resolveWorkspaceIntegration(opts.workspaceId, 'github', opts.repo);
117
+
118
+ return {
119
+ runtime: 'auto',
120
+ nango: {
121
+ connectionId: connection.connectionId,
122
+ providerConfigKey: connection.providerConfigKey, // 'github-agentworkforce' | 'github-msd' | 'github-nightcto' | ...
123
+ secretKey: process.env.NANGO_SECRET_KEY,
124
+ },
125
+ relayCloud: {
126
+ apiUrl: process.env.CLOUD_API_URL,
127
+ accessToken: process.env.CLOUD_API_TOKEN,
128
+ workspaceId: opts.workspaceId,
129
+ },
130
+ };
131
+ }
132
+ ```
133
+
134
+ Workflow authors in cloud then write:
135
+
136
+ ```ts
137
+ createGitHubStep({
138
+ name: 'open-pr',
139
+ action: 'createPR',
140
+ repo: 'AgentWorkforce/cloud',
141
+ params: { title, head, base, body },
142
+ config: await githubConfigForRepo({
143
+ repo: 'AgentWorkforce/cloud',
144
+ workspaceId: process.env.RELAY_WORKSPACE_ID,
145
+ }),
146
+ });
147
+ ```
148
+
149
+ One resolver, one call-site shape. Adding a new GitHub App install is a
150
+ `workspace_integrations` row + (optionally) a `NANGO_*` secret — no
151
+ code change in the workflows that create PRs.
@@ -0,0 +1,49 @@
1
+ # GitHub Primitive Actions
2
+
3
+ All actions use the same adapter request layer, so local and cloud runtimes have
4
+ the same behavior after runtime selection.
5
+
6
+ ## Repository
7
+
8
+ - `listRepos`: lists repositories visible to the authenticated user.
9
+ - `getRepo`: fetches a repository by owner and name.
10
+
11
+ ## Issues
12
+
13
+ - `listIssues`: lists true issues and filters out pull request entries returned
14
+ by the GitHub issues endpoint.
15
+ - `createIssue`: creates an issue.
16
+ - `updateIssue`: updates title, body, state, assignee, or labels.
17
+ - `closeIssue`: closes an issue with the same PATCH path used by
18
+ `updateIssue`.
19
+
20
+ ## Pull Requests
21
+
22
+ - `listPRs`: lists pull requests.
23
+ - `getPR`: fetches one pull request.
24
+ - `createPR`: creates a pull request.
25
+ - `updatePR`: updates title, body, state, base branch, or maintainer edit access.
26
+ - `mergePR`: merges a pull request and then fetches the refreshed pull request.
27
+
28
+ ## Files
29
+
30
+ - `listFiles`: lists directory contents, or returns a single file as a one-item
31
+ list.
32
+ - `readFile`: decodes base64 file content from the GitHub contents API.
33
+ - `createFile`: creates a file with a commit message.
34
+ - `updateFile`: updates a file by SHA with a commit message.
35
+ - `deleteFile`: deletes a file by SHA with a commit message.
36
+
37
+ ## Branches And Commits
38
+
39
+ - `listBranches`: lists repository branches.
40
+ - `createBranch`: creates a branch from `fromBranch` or the repository default
41
+ branch.
42
+ - `listCommits`: lists commits with optional filters.
43
+ - `createCommit`: creates a Git commit object without moving refs.
44
+
45
+ ## Identity
46
+
47
+ - `getUser`: fetches the authenticated user, or a public user by username.
48
+ - `listOrganizations`: lists organizations for the authenticated user, or for a
49
+ public user.
@@ -0,0 +1,286 @@
1
+ /**
2
+ * End-to-end pull-request workflow using the GitHub primitive.
3
+ *
4
+ * Demonstrates the full PR lifecycle from a single workflow definition:
5
+ * 1. Detect + log which runtime will be used (local gh vs Nango vs
6
+ * relay-cloud proxy).
7
+ * 2. createBranch — branch off the default branch for the change.
8
+ * 3. createFile — write a timestamped marker file on the new branch.
9
+ * 4. createPR — open the pull request. This is the core interface the
10
+ * cloud workflows need to swap to.
11
+ * 5. getPR — round-trip verify.
12
+ * 6. updatePR — add a description edit (shows mutation).
13
+ * 7. listPRs — confirm it appears in the default filter.
14
+ * 8. (commented out) mergePR — the last mile. Left off by default so
15
+ * running this example does NOT merge anything against real repos.
16
+ *
17
+ * The same file runs identically in three environments because the
18
+ * primitive's runtime selection handles the transport:
19
+ *
20
+ * - LOCAL gh CLI (needs `gh auth status` to succeed)
21
+ * - CLOUD (tenant) Nango — NANGO_GITHUB_CONNECTION_ID +
22
+ * NANGO_GITHUB_PROVIDER_CONFIG_KEY +
23
+ * NANGO_SECRET_KEY
24
+ * - CLOUD (fallback) relay-cloud proxy — RELAY_CLOUD_API_URL +
25
+ * RELAY_CLOUD_API_TOKEN + WORKSPACE_ID
26
+ *
27
+ * Run:
28
+ * GITHUB_REPO=AgentWorkforce/scratch npx tsx examples/end-to-end-pr-workflow.ts
29
+ *
30
+ * Defaults to AgentWorkforce/scratch (a sandbox repo) so no one
31
+ * accidentally opens a PR against a real repo. Override via env vars.
32
+ */
33
+
34
+ import { WorkflowRunner, type RelayYamlConfig } from '@agent-relay/sdk/workflows';
35
+
36
+ import { GitHubClient } from '../src/client.js';
37
+ import { GitHubStepExecutor, createGitHubStep } from '../src/workflow-step.js';
38
+ import type { GitHubRuntimeConfig } from '../src/types.js';
39
+
40
+ const repo = process.env.GITHUB_REPO ?? 'AgentWorkforce/scratch';
41
+ const baseBranch = process.env.GITHUB_BASE_BRANCH ?? 'main';
42
+ const branchName = process.env.GITHUB_BRANCH_OVERRIDE ?? `examples/github-primitive-${Date.now()}`;
43
+ const markerPath = `examples/github-primitive-runs/${Date.now()}.md`;
44
+
45
+ const githubConfig: GitHubRuntimeConfig = {
46
+ // `auto` prefers cloud credentials when present, otherwise falls back
47
+ // to the local `gh` CLI. Override with GITHUB_RUNTIME=local|cloud when
48
+ // you want to pin a specific path.
49
+ runtime: (process.env.GITHUB_RUNTIME as GitHubRuntimeConfig['runtime']) ?? 'auto',
50
+
51
+ // Cloud path A — Nango. Per-tenant GitHub App installation. Cloud
52
+ // callers typically resolve this via a connection-resolver helper
53
+ // that maps { workspaceId, repo } -> { connectionId, providerConfigKey }.
54
+ nango: {
55
+ connectionId: process.env.NANGO_GITHUB_CONNECTION_ID,
56
+ providerConfigKey: process.env.NANGO_GITHUB_PROVIDER_CONFIG_KEY,
57
+ secretKey: process.env.NANGO_SECRET_KEY,
58
+ },
59
+
60
+ // Cloud path B — relay-cloud GitHub proxy. Used when Nango isn't
61
+ // wired but a relay-cloud bearer token is available.
62
+ relayCloud: {
63
+ apiUrl: process.env.RELAY_CLOUD_API_URL,
64
+ accessToken: process.env.RELAY_CLOUD_API_TOKEN,
65
+ workspaceId: process.env.WORKSPACE_ID,
66
+ },
67
+ };
68
+
69
+ const githubExecutor = new GitHubStepExecutor(githubConfig);
70
+
71
+ const config: RelayYamlConfig = {
72
+ version: '1.0',
73
+ name: 'end-to-end-pr-workflow',
74
+ description:
75
+ 'Walk through the full PR lifecycle — branch, commit, open, update, list — using the GitHub primitive.',
76
+ swarm: { pattern: 'pipeline' },
77
+ agents: [],
78
+ workflows: [
79
+ {
80
+ name: 'end-to-end-pr-workflow',
81
+ steps: [
82
+ // 1. Resolve the default branch — sanity check the connection
83
+ // works before we start making mutations.
84
+ createGitHubStep({
85
+ name: 'inspect-repo',
86
+ action: 'getRepo',
87
+ repo,
88
+ output: {
89
+ mode: 'summary',
90
+ includeRuntime: true,
91
+ pretty: true,
92
+ },
93
+ }),
94
+
95
+ // 2. Create the feature branch off the base branch's HEAD.
96
+ // Chains {{steps.inspect-repo.output.data.defaultBranch}}
97
+ // when no base override is provided — here we keep it
98
+ // explicit for readability.
99
+ createGitHubStep({
100
+ name: 'create-branch',
101
+ dependsOn: ['inspect-repo'],
102
+ action: 'createBranch',
103
+ repo,
104
+ params: {
105
+ branch: branchName,
106
+ source: baseBranch,
107
+ },
108
+ output: { mode: 'data', format: 'json', path: 'ref' },
109
+ }),
110
+
111
+ // 3. Write a marker file on the new branch. createFile handles
112
+ // the blob + tree + commit dance for you.
113
+ createGitHubStep({
114
+ name: 'write-marker-file',
115
+ dependsOn: ['create-branch'],
116
+ action: 'createFile',
117
+ repo,
118
+ params: {
119
+ path: markerPath,
120
+ branch: branchName,
121
+ content: [
122
+ '# GitHub primitive example run',
123
+ '',
124
+ `- Runtime chosen: see workflow log for inspect-repo detection`,
125
+ `- Generated: ${new Date().toISOString()}`,
126
+ '',
127
+ 'This file is created by',
128
+ '`packages/github-primitive/examples/end-to-end-pr-workflow.ts`',
129
+ 'to prove the full PR lifecycle works against the configured runtime.',
130
+ ].join('\n'),
131
+ message: `examples: github-primitive demo run ${new Date().toISOString()}`,
132
+ },
133
+ output: { mode: 'data', format: 'json', path: 'commit.sha' },
134
+ }),
135
+
136
+ // 4. Open the pull request. This is the core step cloud
137
+ // workflows need. title/body/head/base mirror the REST API
138
+ // shape — no translation work at the call site.
139
+ createGitHubStep({
140
+ name: 'open-pr',
141
+ dependsOn: ['write-marker-file'],
142
+ action: 'createPR',
143
+ repo,
144
+ params: {
145
+ title: `examples: github-primitive end-to-end demo (${branchName})`,
146
+ head: branchName,
147
+ base: baseBranch,
148
+ body: [
149
+ '## Summary',
150
+ '',
151
+ 'Automated PR opened by',
152
+ '`packages/github-primitive/examples/end-to-end-pr-workflow.ts`',
153
+ 'to exercise the GitHub primitive interface end-to-end.',
154
+ '',
155
+ '## Runtime selection',
156
+ '',
157
+ 'See the workflow log for the `inspect-repo` step — it logs the',
158
+ 'selected runtime (`local`, `nango`, or `relay-cloud`).',
159
+ '',
160
+ '## Safe to close',
161
+ '',
162
+ 'This PR is a demonstration. No one should merge it — close it',
163
+ 'once you have inspected the end-to-end round-trip.',
164
+ ].join('\n'),
165
+ draft: true,
166
+ },
167
+ output: {
168
+ mode: 'data',
169
+ format: 'json',
170
+ includeRuntime: true,
171
+ includeMetadata: true,
172
+ pretty: true,
173
+ },
174
+ }),
175
+
176
+ // 5. Read the PR back to prove the resolver + runtime actually
177
+ // persisted the change, and to surface the PR number for
178
+ // downstream steps.
179
+ createGitHubStep({
180
+ name: 'verify-pr',
181
+ dependsOn: ['open-pr'],
182
+ action: 'getPR',
183
+ repo,
184
+ params: {
185
+ // The output of `open-pr` is the Pulls REST response; we pull
186
+ // the number off it for subsequent mutation.
187
+ number: '{{steps.open-pr.output.data.number}}',
188
+ },
189
+ output: {
190
+ mode: 'summary',
191
+ includeRuntime: true,
192
+ pretty: true,
193
+ },
194
+ }),
195
+
196
+ // 6. Update the PR — shows how to use updatePR for edits that
197
+ // don't need code changes (body, title, draft state, etc.).
198
+ createGitHubStep({
199
+ name: 'edit-pr-body',
200
+ dependsOn: ['verify-pr'],
201
+ action: 'updatePR',
202
+ repo,
203
+ params: {
204
+ number: '{{steps.open-pr.output.data.number}}',
205
+ body: [
206
+ '## Summary',
207
+ '',
208
+ 'Automated PR opened by the GitHub primitive end-to-end',
209
+ 'example. This body was updated by the `edit-pr-body` step',
210
+ 'to demonstrate `updatePR` works through the same adapter.',
211
+ '',
212
+ '## Safe to close',
213
+ '',
214
+ 'Demo — close, do not merge.',
215
+ ].join('\n'),
216
+ },
217
+ output: { mode: 'result', format: 'json' },
218
+ }),
219
+
220
+ // 7. List open PRs to prove the new one is indexable.
221
+ createGitHubStep({
222
+ name: 'list-open-prs',
223
+ dependsOn: ['edit-pr-body'],
224
+ action: 'listPRs',
225
+ repo,
226
+ params: {
227
+ state: 'open',
228
+ perPage: 10,
229
+ },
230
+ output: { mode: 'summary', pretty: true },
231
+ }),
232
+
233
+ // 8. (Commented) The last mile — merge. Intentionally left off:
234
+ // running this example against a real repo should not merge
235
+ // anything. Uncomment when exercising a disposable scratch
236
+ // repo or CI harness.
237
+ //
238
+ // createGitHubStep({
239
+ // name: 'merge-pr',
240
+ // dependsOn: ['list-open-prs'],
241
+ // action: 'mergePR',
242
+ // repo,
243
+ // params: {
244
+ // number: '{{steps.open-pr.output.data.number}}',
245
+ // mergeMethod: 'squash',
246
+ // commitTitle: 'examples: github-primitive demo (squash)',
247
+ // },
248
+ // output: { mode: 'data', format: 'json' },
249
+ // }),
250
+ ],
251
+ },
252
+ ],
253
+ errorHandling: { strategy: 'fail-fast' },
254
+ };
255
+
256
+ async function main(): Promise<void> {
257
+ const detection = await GitHubClient.detect(githubConfig);
258
+
259
+ console.log('────────────────────────────────────────');
260
+ console.log(`repo: ${repo}`);
261
+ console.log(`base branch: ${baseBranch}`);
262
+ console.log(`feature branch: ${branchName}`);
263
+ console.log(`runtime selected: ${detection.runtime}`);
264
+ console.log(`detection source: ${detection.source}`);
265
+ console.log(`local gh available: ${detection.local.available}`);
266
+ console.log(`cloud available: ${detection.cloud.available}`);
267
+ if (detection.reason) {
268
+ console.log(`reason: ${detection.reason}`);
269
+ }
270
+ console.log('────────────────────────────────────────');
271
+
272
+ const runner = new WorkflowRunner({
273
+ cwd: process.cwd(),
274
+ executor: githubExecutor,
275
+ });
276
+
277
+ const result = await runner.execute(config);
278
+ console.log(`\nWorkflow completed: ${result.status}`);
279
+ }
280
+
281
+ if (process.argv[1] === new URL(import.meta.url).pathname) {
282
+ main().catch((error) => {
283
+ console.error(error instanceof Error ? error.stack : error);
284
+ process.exitCode = 1;
285
+ });
286
+ }
@@ -0,0 +1,38 @@
1
+ import { GitHubClient } from '../src/client.js';
2
+ import type { GitHubRuntimeConfig } from '../src/types.js';
3
+
4
+ const [owner = 'AgentWorkforce', repo = 'relay'] = (process.env.GITHUB_REPO || 'AgentWorkforce/relay').split(
5
+ '/'
6
+ );
7
+
8
+ const githubConfig: GitHubRuntimeConfig = {
9
+ runtime: (process.env.GITHUB_RUNTIME as GitHubRuntimeConfig['runtime']) ?? 'auto',
10
+ nango: {
11
+ connectionId: process.env.NANGO_GITHUB_CONNECTION_ID,
12
+ providerConfigKey: process.env.NANGO_GITHUB_PROVIDER_CONFIG_KEY,
13
+ },
14
+ relayCloud: {
15
+ apiUrl: process.env.RELAY_CLOUD_API_URL,
16
+ accessToken: process.env.RELAY_CLOUD_API_TOKEN,
17
+ workspaceId: process.env.WORKSPACE_ID,
18
+ },
19
+ };
20
+
21
+ async function main(): Promise<void> {
22
+ const github = await GitHubClient.create(githubConfig);
23
+ const runtime = await github.getRuntime();
24
+ const repository = await github.getRepo(owner, repo);
25
+ const branches = await github.listBranches(owner, repo);
26
+
27
+ console.log(`Runtime: ${runtime}`);
28
+ console.log(`Repository: ${repository.fullName}`);
29
+ console.log(`Default branch: ${repository.defaultBranch}`);
30
+ console.log(`Branches: ${branches.map((branch) => branch.name).join(', ')}`);
31
+ }
32
+
33
+ if (process.argv[1] === new URL(import.meta.url).pathname) {
34
+ main().catch((error) => {
35
+ console.error(error instanceof Error ? error.stack : error);
36
+ process.exitCode = 1;
37
+ });
38
+ }
@@ -0,0 +1,105 @@
1
+ import { WorkflowRunner, type RelayYamlConfig } from '@agent-relay/sdk/workflows';
2
+
3
+ import { GitHubClient } from '../src/client.js';
4
+ import { GitHubStepExecutor, createGitHubStep } from '../src/workflow-step.js';
5
+ import type { GitHubRuntimeConfig } from '../src/types.js';
6
+
7
+ const repo = process.env.GITHUB_REPO ?? 'AgentWorkforce/relay';
8
+
9
+ const githubConfig: GitHubRuntimeConfig = {
10
+ runtime: (process.env.GITHUB_RUNTIME as GitHubRuntimeConfig['runtime']) ?? 'auto',
11
+ nango: {
12
+ connectionId: process.env.NANGO_GITHUB_CONNECTION_ID,
13
+ providerConfigKey: process.env.NANGO_GITHUB_PROVIDER_CONFIG_KEY,
14
+ },
15
+ relayCloud: {
16
+ apiUrl: process.env.RELAY_CLOUD_API_URL,
17
+ accessToken: process.env.RELAY_CLOUD_API_TOKEN,
18
+ workspaceId: process.env.WORKSPACE_ID,
19
+ },
20
+ };
21
+
22
+ const githubExecutor = new GitHubStepExecutor(githubConfig);
23
+
24
+ const config: RelayYamlConfig = {
25
+ version: '1.0',
26
+ name: 'github-primitive-workflow',
27
+ description: 'GitHub primitive workflow with runtime auto-detection and chained output.',
28
+ swarm: {
29
+ pattern: 'pipeline',
30
+ },
31
+ agents: [],
32
+ workflows: [
33
+ {
34
+ name: 'github-primitive-workflow',
35
+ steps: [
36
+ createGitHubStep({
37
+ name: 'inspect-repository',
38
+ action: 'getRepo',
39
+ repo,
40
+ output: {
41
+ mode: 'summary',
42
+ includeRuntime: true,
43
+ pretty: true,
44
+ },
45
+ }),
46
+ createGitHubStep({
47
+ name: 'list-open-issues',
48
+ dependsOn: ['inspect-repository'],
49
+ action: 'listIssues',
50
+ repo,
51
+ params: {
52
+ state: 'open',
53
+ perPage: 5,
54
+ },
55
+ output: {
56
+ mode: 'summary',
57
+ includeRuntime: true,
58
+ pretty: true,
59
+ },
60
+ }),
61
+ createGitHubStep({
62
+ name: 'read-readme',
63
+ dependsOn: ['list-open-issues'],
64
+ action: 'readFile',
65
+ repo,
66
+ params: {
67
+ path: 'README.md',
68
+ },
69
+ output: {
70
+ mode: 'data',
71
+ format: 'text',
72
+ },
73
+ }),
74
+ ],
75
+ },
76
+ ],
77
+ errorHandling: {
78
+ strategy: 'fail-fast',
79
+ },
80
+ };
81
+
82
+ async function main(): Promise<void> {
83
+ const detection = await GitHubClient.detect(githubConfig);
84
+
85
+ console.log(`GitHub runtime selected: ${detection.runtime}`);
86
+ console.log(`Detection source: ${detection.source}`);
87
+ console.log(`Local gh CLI: ${detection.local.available ? 'available' : 'unavailable'}`);
88
+ console.log(`Cloud GitHub: ${detection.cloud.available ? 'available' : 'unavailable'}`);
89
+ console.log(detection.reason);
90
+
91
+ const runner = new WorkflowRunner({
92
+ cwd: process.cwd(),
93
+ executor: githubExecutor,
94
+ });
95
+
96
+ const result = await runner.execute(config);
97
+ console.log(`GitHub workflow completed: ${result.status}`);
98
+ }
99
+
100
+ if (process.argv[1] === new URL(import.meta.url).pathname) {
101
+ main().catch((error) => {
102
+ console.error(error instanceof Error ? error.stack : error);
103
+ process.exitCode = 1;
104
+ });
105
+ }