@blogic-cz/agent-tools 0.14.24 → 0.14.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blogic-cz/agent-tools",
3
- "version": "0.14.24",
3
+ "version": "0.14.25",
4
4
  "description": "CLI tools for AI coding agent workflows — GitHub, database, Kubernetes, Azure DevOps, logs, sessions, and audit",
5
5
  "keywords": [
6
6
  "agent",
@@ -133,13 +133,13 @@
133
133
  "test": "vitest run"
134
134
  },
135
135
  "dependencies": {
136
- "@effect/platform-bun": "4.0.0-beta.65",
136
+ "@effect/platform-bun": "4.0.0-beta.74",
137
137
  "@toon-format/toon": "2.1.0",
138
- "effect": "4.0.0-beta.65"
138
+ "effect": "4.0.0-beta.74"
139
139
  },
140
140
  "devDependencies": {
141
- "@effect/language-service": "0.85.1",
142
- "@effect/vitest": "4.0.0-beta.65",
141
+ "@effect/language-service": "0.86.2",
142
+ "@effect/vitest": "4.0.0-beta.74",
143
143
  "@types/bun": "1.3.12",
144
144
  "oxfmt": "0.44.0",
145
145
  "oxlint": "1.59.0",
@@ -147,7 +147,7 @@
147
147
  "vitest": "^4.1.4"
148
148
  },
149
149
  "overrides": {
150
- "@effect/platform-node-shared": "4.0.0-beta.65"
150
+ "@effect/platform-node-shared": "4.0.0-beta.74"
151
151
  },
152
152
  "engines": {
153
153
  "bun": ">=1.0.0"
@@ -22,5 +22,6 @@ export {
22
22
  getToolConfig,
23
23
  getDefaultEnvironment,
24
24
  getGitHubConfig,
25
+ resolveGitHubRepoTarget,
25
26
  loadConfig,
26
27
  } from "./loader";
@@ -318,10 +318,36 @@ export function getGitHubConfig(
318
318
  if (keys.length === 0) return undefined;
319
319
 
320
320
  if (profile) return repos[profile];
321
- if (keys.length === 1) return repos[keys[0] ?? ""];
322
321
  if ("default" in repos) return repos.default;
322
+ if (keys.length === 1) return repos[keys[0] ?? ""];
323
323
 
324
324
  throw new Error(
325
325
  `Multiple github profiles found: [${keys.join(", ")}]. Use --repo <name> to select one.`,
326
326
  );
327
327
  }
328
+
329
+ export function resolveGitHubRepoTarget(
330
+ config: AgentToolsConfig | undefined,
331
+ target?: string | null,
332
+ ): string | undefined {
333
+ if (target && target.includes("/")) {
334
+ return target;
335
+ }
336
+
337
+ const repos = config?.github;
338
+ if (!repos) return target ?? undefined;
339
+
340
+ const keys = Object.keys(repos);
341
+ if (target) {
342
+ const repo = repos[target];
343
+ if (!repo) {
344
+ throw new Error(
345
+ `Unknown github profile '${target}'. Available profiles: [${keys.join(", ")}]. Use --repo owner/name for an explicit repository.`,
346
+ );
347
+ }
348
+ return `${repo.owner}/${repo.repo}`;
349
+ }
350
+
351
+ const repo = getGitHubConfig(config);
352
+ return repo ? `${repo.owner}/${repo.repo}` : undefined;
353
+ }
@@ -105,14 +105,14 @@ export const issueCloseCommand = Command.make(
105
105
  },
106
106
  ({ comment, commentFile, format, issue, reason }) =>
107
107
  Effect.gen(function* () {
108
- const resolvedComment = yield* resolveOptionalTextInput(
109
- "gh-tool issue close",
110
- Option.getOrNull(comment),
111
- Option.getOrNull(commentFile),
112
- "--comment",
113
- "--comment-file",
114
- "comment",
115
- );
108
+ const resolvedComment = yield* resolveOptionalTextInput({
109
+ command: "gh-tool issue close",
110
+ value: Option.getOrNull(comment),
111
+ fileValue: Option.getOrNull(commentFile),
112
+ valueFlag: "--comment",
113
+ fileFlag: "--comment-file",
114
+ label: "comment",
115
+ });
116
116
 
117
117
  const result = yield* closeIssue({
118
118
  comment: resolvedComment,
@@ -139,14 +139,14 @@ export const issueReopenCommand = Command.make(
139
139
  },
140
140
  ({ comment, commentFile, format, issue }) =>
141
141
  Effect.gen(function* () {
142
- const resolvedComment = yield* resolveOptionalTextInput(
143
- "gh-tool issue reopen",
144
- Option.getOrNull(comment),
145
- Option.getOrNull(commentFile),
146
- "--comment",
147
- "--comment-file",
148
- "comment",
149
- );
142
+ const resolvedComment = yield* resolveOptionalTextInput({
143
+ command: "gh-tool issue reopen",
144
+ value: Option.getOrNull(comment),
145
+ fileValue: Option.getOrNull(commentFile),
146
+ valueFlag: "--comment",
147
+ fileFlag: "--comment-file",
148
+ label: "comment",
149
+ });
150
150
 
151
151
  const result = yield* reopenIssue({
152
152
  comment: resolvedComment,
@@ -169,14 +169,14 @@ export const issueCommentCommand = Command.make(
169
169
  },
170
170
  ({ body, bodyFile, format, issue }) =>
171
171
  Effect.gen(function* () {
172
- const resolvedBody = yield* resolveRequiredTextInput(
173
- "gh-tool issue comment",
174
- Option.getOrNull(body),
175
- Option.getOrNull(bodyFile),
176
- "--body",
177
- "--body-file",
178
- "body",
179
- );
172
+ const resolvedBody = yield* resolveRequiredTextInput({
173
+ command: "gh-tool issue comment",
174
+ value: Option.getOrNull(body),
175
+ fileValue: Option.getOrNull(bodyFile),
176
+ valueFlag: "--body",
177
+ fileFlag: "--body-file",
178
+ label: "body",
179
+ });
180
180
 
181
181
  const result = yield* commentOnIssue({ body: resolvedBody, issue });
182
182
  yield* logFormatted(result, format);
@@ -223,14 +223,14 @@ export const issueEditCommand = Command.make(
223
223
  title,
224
224
  }) =>
225
225
  Effect.gen(function* () {
226
- const resolvedBody = yield* resolveOptionalTextInput(
227
- "gh-tool issue edit",
228
- Option.getOrNull(body),
229
- Option.getOrNull(bodyFile),
230
- "--body",
231
- "--body-file",
232
- "body",
233
- );
226
+ const resolvedBody = yield* resolveOptionalTextInput({
227
+ command: "gh-tool issue edit",
228
+ value: Option.getOrNull(body),
229
+ fileValue: Option.getOrNull(bodyFile),
230
+ valueFlag: "--body",
231
+ fileFlag: "--body-file",
232
+ label: "body",
233
+ });
234
234
 
235
235
  const result = yield* editIssue({
236
236
  addAssignee: Option.getOrNull(addAssignee),
@@ -4,6 +4,7 @@ import { Effect, Option } from "effect";
4
4
  import type { PRStatusResult } from "#gh/types";
5
5
 
6
6
  import { formatOption, logFormatted } from "#shared";
7
+ import { GitHubService } from "#gh/service";
7
8
  import {
8
9
  resolveDefaultTextInput,
9
10
  resolveOptionalTextInput,
@@ -44,6 +45,17 @@ import {
44
45
  // CLI Commands
45
46
  // ---------------------------------------------------------------------------
46
47
 
48
+ const repoOption = Flag.string("repo").pipe(
49
+ Flag.withDescription("Target repository profile name or owner/name"),
50
+ Flag.optional,
51
+ );
52
+
53
+ const withRepo = <A, E, R>(repo: Option.Option<string>, effect: Effect.Effect<A, E, R>) =>
54
+ Effect.gen(function* () {
55
+ const gh = yield* GitHubService;
56
+ return yield* gh.withRepoTarget(Option.getOrNull(repo), effect);
57
+ });
58
+
47
59
  export const prViewCommand = Command.make(
48
60
  "view",
49
61
  {
@@ -52,20 +64,30 @@ export const prViewCommand = Command.make(
52
64
  Flag.withDescription("PR number (default: current branch PR)"),
53
65
  Flag.optional,
54
66
  ),
67
+ repo: repoOption,
55
68
  },
56
- ({ format, pr }) =>
57
- Effect.gen(function* () {
58
- const prNumber = Option.getOrNull(pr);
59
- const info = yield* viewPR(prNumber);
60
- yield* logFormatted(info, format);
61
- }),
69
+ ({ format, pr, repo }) =>
70
+ withRepo(
71
+ repo,
72
+ Effect.gen(function* () {
73
+ const prNumber = Option.getOrNull(pr);
74
+ const info = yield* viewPR(prNumber);
75
+ yield* logFormatted(info, format);
76
+ }),
77
+ ),
62
78
  ).pipe(Command.withDescription("View PR information"));
63
79
 
64
- export const prStatusCommand = Command.make("status", { format: formatOption }, ({ format }) =>
65
- Effect.gen(function* () {
66
- const result: PRStatusResult = yield* detectPRStatus();
67
- yield* logFormatted(result, format);
68
- }),
80
+ export const prStatusCommand = Command.make(
81
+ "status",
82
+ { format: formatOption, repo: repoOption },
83
+ ({ format, repo }) =>
84
+ withRepo(
85
+ repo,
86
+ Effect.gen(function* () {
87
+ const result: PRStatusResult = yield* detectPRStatus();
88
+ yield* logFormatted(result, format);
89
+ }),
90
+ ),
69
91
  ).pipe(
70
92
  Command.withDescription("Auto-detect PR for current branch or GitButler workspace branches"),
71
93
  );
@@ -82,6 +104,10 @@ export const prCreateCommand = Command.make(
82
104
  Flag.withDescription("Read PR body from a file path or '-' for stdin"),
83
105
  Flag.optional,
84
106
  ),
107
+ bodyStdin: Flag.boolean("body-stdin").pipe(
108
+ Flag.withDescription("Read PR body from stdin"),
109
+ Flag.withDefault(false),
110
+ ),
85
111
  draft: Flag.boolean("draft").pipe(
86
112
  Flag.withDescription("Create as draft PR"),
87
113
  Flag.withDefault(false),
@@ -91,29 +117,35 @@ export const prCreateCommand = Command.make(
91
117
  Flag.withDescription("Source branch name (required in GitButler workspace mode)"),
92
118
  Flag.optional,
93
119
  ),
120
+ repo: repoOption,
94
121
  title: Flag.string("title").pipe(Flag.withDescription("PR title")),
95
122
  },
96
- ({ base, body, bodyFile, draft, format, head, title }) =>
97
- Effect.gen(function* () {
98
- const resolvedBody = yield* resolveDefaultTextInput(
99
- "gh-tool pr create",
100
- Option.getOrNull(body),
101
- Option.getOrNull(bodyFile),
102
- "--body",
103
- "--body-file",
104
- "body",
105
- "",
106
- );
123
+ ({ base, body, bodyFile, bodyStdin, draft, format, head, repo, title }) =>
124
+ withRepo(
125
+ repo,
126
+ Effect.gen(function* () {
127
+ const resolvedBody = yield* resolveDefaultTextInput({
128
+ command: "gh-tool pr create",
129
+ value: Option.getOrNull(body),
130
+ fileValue: Option.getOrNull(bodyFile),
131
+ stdin: bodyStdin,
132
+ valueFlag: "--body",
133
+ fileFlag: "--body-file",
134
+ stdinFlag: "--body-stdin",
135
+ label: "body",
136
+ defaultValue: "",
137
+ });
107
138
 
108
- const info = yield* createPR({
109
- base,
110
- body: resolvedBody,
111
- draft,
112
- head: Option.getOrNull(head),
113
- title,
114
- });
115
- yield* logFormatted(info, format);
116
- }),
139
+ const info = yield* createPR({
140
+ base,
141
+ body: resolvedBody,
142
+ draft,
143
+ head: Option.getOrNull(head),
144
+ title,
145
+ });
146
+ yield* logFormatted(info, format);
147
+ }),
148
+ ),
117
149
  ).pipe(Command.withDescription("Create or update a PR for current branch"));
118
150
 
119
151
  export const prEditCommand = Command.make(
@@ -125,29 +157,39 @@ export const prEditCommand = Command.make(
125
157
  Flag.withDescription("Read PR body from a file path or '-' for stdin"),
126
158
  Flag.optional,
127
159
  ),
160
+ bodyStdin: Flag.boolean("body-stdin").pipe(
161
+ Flag.withDescription("Read PR body from stdin"),
162
+ Flag.withDefault(false),
163
+ ),
128
164
  format: formatOption,
129
165
  pr: Flag.integer("pr").pipe(Flag.withDescription("PR number to edit")),
166
+ repo: repoOption,
130
167
  title: Flag.string("title").pipe(Flag.withDescription("New PR title"), Flag.optional),
131
168
  },
132
- ({ base, body, bodyFile, format, pr, title }) =>
133
- Effect.gen(function* () {
134
- const resolvedBody = yield* resolveOptionalTextInput(
135
- "gh-tool pr edit",
136
- Option.getOrNull(body),
137
- Option.getOrNull(bodyFile),
138
- "--body",
139
- "--body-file",
140
- "body",
141
- );
169
+ ({ base, body, bodyFile, bodyStdin, format, pr, repo, title }) =>
170
+ withRepo(
171
+ repo,
172
+ Effect.gen(function* () {
173
+ const resolvedBody = yield* resolveOptionalTextInput({
174
+ command: "gh-tool pr edit",
175
+ value: Option.getOrNull(body),
176
+ fileValue: Option.getOrNull(bodyFile),
177
+ stdin: bodyStdin,
178
+ valueFlag: "--body",
179
+ fileFlag: "--body-file",
180
+ stdinFlag: "--body-stdin",
181
+ label: "body",
182
+ });
142
183
 
143
- const info = yield* editPR({
144
- pr,
145
- title: Option.getOrNull(title),
146
- body: resolvedBody,
147
- base: Option.getOrNull(base),
148
- });
149
- yield* logFormatted(info, format);
150
- }),
184
+ const info = yield* editPR({
185
+ pr,
186
+ title: Option.getOrNull(title),
187
+ body: resolvedBody,
188
+ base: Option.getOrNull(base),
189
+ });
190
+ yield* logFormatted(info, format);
191
+ }),
192
+ ),
151
193
  ).pipe(Command.withDescription("Edit an existing PR's title, body, or other metadata"));
152
194
 
153
195
  export const prCloseCommand = Command.make(
@@ -167,25 +209,29 @@ export const prCloseCommand = Command.make(
167
209
  ),
168
210
  format: formatOption,
169
211
  pr: Flag.integer("pr").pipe(Flag.withDescription("PR number to close")),
212
+ repo: repoOption,
170
213
  },
171
- ({ comment, commentFile, deleteBranch, format, pr }) =>
172
- Effect.gen(function* () {
173
- const resolvedComment = yield* resolveOptionalTextInput(
174
- "gh-tool pr close",
175
- Option.getOrNull(comment),
176
- Option.getOrNull(commentFile),
177
- "--comment",
178
- "--comment-file",
179
- "comment",
180
- );
214
+ ({ comment, commentFile, deleteBranch, format, pr, repo }) =>
215
+ withRepo(
216
+ repo,
217
+ Effect.gen(function* () {
218
+ const resolvedComment = yield* resolveOptionalTextInput({
219
+ command: "gh-tool pr close",
220
+ value: Option.getOrNull(comment),
221
+ fileValue: Option.getOrNull(commentFile),
222
+ valueFlag: "--comment",
223
+ fileFlag: "--comment-file",
224
+ label: "comment",
225
+ });
181
226
 
182
- const result = yield* closePR({
183
- comment: resolvedComment,
184
- deleteBranch,
185
- pr,
186
- });
187
- yield* logFormatted(result, format);
188
- }),
227
+ const result = yield* closePR({
228
+ comment: resolvedComment,
229
+ deleteBranch,
230
+ pr,
231
+ });
232
+ yield* logFormatted(result, format);
233
+ }),
234
+ ),
189
235
  ).pipe(Command.withDescription("Close a PR with optional comment and branch deletion"));
190
236
 
191
237
  export const prMergeCommand = Command.make(
@@ -201,21 +247,25 @@ export const prMergeCommand = Command.make(
201
247
  ),
202
248
  format: formatOption,
203
249
  pr: Flag.integer("pr").pipe(Flag.withDescription("PR number to merge")),
250
+ repo: repoOption,
204
251
  strategy: Flag.choice("strategy", MERGE_STRATEGIES).pipe(
205
252
  Flag.withDescription("Merge strategy: squash, merge, or rebase"),
206
253
  Flag.withDefault(DEFAULT_MERGE_STRATEGY),
207
254
  ),
208
255
  },
209
- ({ confirm, deleteBranch, format, pr, strategy }) =>
210
- Effect.gen(function* () {
211
- const result = yield* mergePR({
212
- confirm,
213
- deleteBranch,
214
- pr,
215
- strategy,
216
- });
217
- yield* logFormatted(result, format);
218
- }),
256
+ ({ confirm, deleteBranch, format, pr, repo, strategy }) =>
257
+ withRepo(
258
+ repo,
259
+ Effect.gen(function* () {
260
+ const result = yield* mergePR({
261
+ confirm,
262
+ deleteBranch,
263
+ pr,
264
+ strategy,
265
+ });
266
+ yield* logFormatted(result, format);
267
+ }),
268
+ ),
219
269
  ).pipe(Command.withDescription("Merge a PR (dry-run by default, use --confirm to execute)"));
220
270
 
221
271
  export const prChecksCommand = Command.make(
@@ -230,6 +280,7 @@ export const prChecksCommand = Command.make(
230
280
  Flag.withDescription("PR number (default: current branch PR)"),
231
281
  Flag.optional,
232
282
  ),
283
+ repo: repoOption,
233
284
  timeout: Flag.integer("timeout").pipe(
234
285
  Flag.withDefault(CI_CHECK_WATCH_TIMEOUT_MS / 1000),
235
286
  Flag.withDescription("Timeout in seconds for watch mode (default: 600)"),
@@ -239,12 +290,15 @@ export const prChecksCommand = Command.make(
239
290
  Flag.withDescription("Watch until checks complete or timeout"),
240
291
  ),
241
292
  },
242
- ({ failFast, format, pr, timeout, watch }) =>
243
- Effect.gen(function* () {
244
- const prNumber = Option.getOrNull(pr);
245
- const checks = yield* fetchChecksForCommand(prNumber, watch, failFast, timeout);
246
- yield* logFormatted(checks, format);
247
- }),
293
+ ({ failFast, format, pr, repo, timeout, watch }) =>
294
+ withRepo(
295
+ repo,
296
+ Effect.gen(function* () {
297
+ const prNumber = Option.getOrNull(pr);
298
+ const checks = yield* fetchChecksForCommand(prNumber, watch, failFast, timeout);
299
+ yield* logFormatted(checks, format);
300
+ }),
301
+ ),
248
302
  ).pipe(Command.withDescription("Fetch CI check status for a PR (optionally watch with timeout)"));
249
303
 
250
304
  export const prChecksFailedCommand = Command.make(
@@ -255,13 +309,17 @@ export const prChecksFailedCommand = Command.make(
255
309
  Flag.withDescription("PR number (default: current branch PR)"),
256
310
  Flag.optional,
257
311
  ),
312
+ repo: repoOption,
258
313
  },
259
- ({ format, pr }) =>
260
- Effect.gen(function* () {
261
- const prNumber = Option.getOrNull(pr);
262
- const checks = yield* fetchFailedChecks(prNumber);
263
- yield* logFormatted(checks, format);
264
- }),
314
+ ({ format, pr, repo }) =>
315
+ withRepo(
316
+ repo,
317
+ Effect.gen(function* () {
318
+ const prNumber = Option.getOrNull(pr);
319
+ const checks = yield* fetchFailedChecks(prNumber);
320
+ yield* logFormatted(checks, format);
321
+ }),
322
+ ),
265
323
  ).pipe(Command.withDescription("Fetch only failed CI checks for a PR"));
266
324
 
267
325
  export const prRerunChecksCommand = Command.make(
@@ -272,17 +330,21 @@ export const prRerunChecksCommand = Command.make(
272
330
  Flag.withDescription("PR number (default: current branch PR)"),
273
331
  Flag.optional,
274
332
  ),
333
+ repo: repoOption,
275
334
  failedOnly: Flag.boolean("failed-only").pipe(
276
335
  Flag.withDefault(true),
277
336
  Flag.withDescription("Only rerun failed checks (default: true)"),
278
337
  ),
279
338
  },
280
- ({ failedOnly, format, pr }) =>
281
- Effect.gen(function* () {
282
- const prNumber = Option.getOrNull(pr);
283
- const result = yield* rerunChecks(prNumber, failedOnly);
284
- yield* logFormatted(result, format);
285
- }),
339
+ ({ failedOnly, format, pr, repo }) =>
340
+ withRepo(
341
+ repo,
342
+ Effect.gen(function* () {
343
+ const prNumber = Option.getOrNull(pr);
344
+ const result = yield* rerunChecks(prNumber, failedOnly);
345
+ yield* logFormatted(result, format);
346
+ }),
347
+ ),
286
348
  ).pipe(
287
349
  Command.withDescription("Rerun CI checks for a PR (GitHub Actions only, failed by default)"),
288
350
  );
@@ -295,6 +357,7 @@ export const prThreadsCommand = Command.make(
295
357
  Flag.withDescription("PR number (default: current branch PR)"),
296
358
  Flag.optional,
297
359
  ),
360
+ repo: repoOption,
298
361
  unresolvedOnly: Flag.boolean("unresolved-only").pipe(
299
362
  Flag.withDescription("Only show unresolved threads"),
300
363
  Flag.withDefault(true),
@@ -306,12 +369,15 @@ export const prThreadsCommand = Command.make(
306
369
  Flag.withDefault(false),
307
370
  ),
308
371
  },
309
- ({ format, pr, unresolvedOnly, visibleOpenOnly }) =>
310
- Effect.gen(function* () {
311
- const prNumber = Option.getOrNull(pr);
312
- const threads = yield* fetchThreads(prNumber, unresolvedOnly, visibleOpenOnly);
313
- yield* logFormatted(threads, format);
314
- }),
372
+ ({ format, pr, repo, unresolvedOnly, visibleOpenOnly }) =>
373
+ withRepo(
374
+ repo,
375
+ Effect.gen(function* () {
376
+ const prNumber = Option.getOrNull(pr);
377
+ const threads = yield* fetchThreads(prNumber, unresolvedOnly, visibleOpenOnly);
378
+ yield* logFormatted(threads, format);
379
+ }),
380
+ ),
315
381
  ).pipe(
316
382
  Command.withDescription(
317
383
  "Fetch review threads for a PR (unresolved by default, or use --visible-open-only for reply-aware human-visible open items)",
@@ -326,18 +392,22 @@ export const prCommentsCommand = Command.make(
326
392
  Flag.withDescription("PR number (default: current branch PR)"),
327
393
  Flag.optional,
328
394
  ),
395
+ repo: repoOption,
329
396
  since: Flag.string("since").pipe(
330
397
  Flag.withDescription("ISO timestamp to filter comments created after"),
331
398
  Flag.optional,
332
399
  ),
333
400
  },
334
- ({ format, pr, since }) =>
335
- Effect.gen(function* () {
336
- const prNumber = Option.getOrNull(pr);
337
- const sinceValue = Option.getOrNull(since);
338
- const comments = yield* fetchComments(prNumber, sinceValue);
339
- yield* logFormatted(comments, format);
340
- }),
401
+ ({ format, pr, repo, since }) =>
402
+ withRepo(
403
+ repo,
404
+ Effect.gen(function* () {
405
+ const prNumber = Option.getOrNull(pr);
406
+ const sinceValue = Option.getOrNull(since);
407
+ const comments = yield* fetchComments(prNumber, sinceValue);
408
+ yield* logFormatted(comments, format);
409
+ }),
410
+ ),
341
411
  ).pipe(Command.withDescription("Fetch review comments for a PR (optionally filter by --since)"));
342
412
 
343
413
  export const prIssueCommentsCommand = Command.make(
@@ -356,26 +426,30 @@ export const prIssueCommentsCommand = Command.make(
356
426
  Flag.withDescription("PR number (default: current branch PR)"),
357
427
  Flag.optional,
358
428
  ),
429
+ repo: repoOption,
359
430
  since: Flag.string("since").pipe(
360
431
  Flag.withDescription("ISO timestamp to filter comments created after"),
361
432
  Flag.optional,
362
433
  ),
363
434
  },
364
- ({ author, bodyContains, format, pr, since }) =>
365
- Effect.gen(function* () {
366
- const prNumber = Option.getOrNull(pr);
367
- const sinceValue = Option.getOrNull(since);
368
- const authorValue = Option.getOrNull(author);
369
- const bodyContainsValue = Option.getOrNull(bodyContains);
435
+ ({ author, bodyContains, format, pr, repo, since }) =>
436
+ withRepo(
437
+ repo,
438
+ Effect.gen(function* () {
439
+ const prNumber = Option.getOrNull(pr);
440
+ const sinceValue = Option.getOrNull(since);
441
+ const authorValue = Option.getOrNull(author);
442
+ const bodyContainsValue = Option.getOrNull(bodyContains);
370
443
 
371
- const comments = yield* fetchIssueComments(
372
- prNumber,
373
- sinceValue,
374
- authorValue,
375
- bodyContainsValue,
376
- );
377
- yield* logFormatted(comments, format);
378
- }),
444
+ const comments = yield* fetchIssueComments(
445
+ prNumber,
446
+ sinceValue,
447
+ authorValue,
448
+ bodyContainsValue,
449
+ );
450
+ yield* logFormatted(comments, format);
451
+ }),
452
+ ),
379
453
  ).pipe(Command.withDescription("Fetch general PR discussion comments (issue comments)"));
380
454
 
381
455
  export const prIssueCommentsLatestCommand = Command.make(
@@ -394,16 +468,20 @@ export const prIssueCommentsLatestCommand = Command.make(
394
468
  Flag.withDescription("PR number (default: current branch PR)"),
395
469
  Flag.optional,
396
470
  ),
471
+ repo: repoOption,
397
472
  },
398
- ({ author, bodyContains, format, pr }) =>
399
- Effect.gen(function* () {
400
- const prNumber = Option.getOrNull(pr);
401
- const authorValue = Option.getOrNull(author);
402
- const bodyContainsValue = Option.getOrNull(bodyContains);
473
+ ({ author, bodyContains, format, pr, repo }) =>
474
+ withRepo(
475
+ repo,
476
+ Effect.gen(function* () {
477
+ const prNumber = Option.getOrNull(pr);
478
+ const authorValue = Option.getOrNull(author);
479
+ const bodyContainsValue = Option.getOrNull(bodyContains);
403
480
 
404
- const comment = yield* fetchLatestIssueComment(prNumber, authorValue, bodyContainsValue);
405
- yield* logFormatted(comment, format);
406
- }),
481
+ const comment = yield* fetchLatestIssueComment(prNumber, authorValue, bodyContainsValue);
482
+ yield* logFormatted(comment, format);
483
+ }),
484
+ ),
407
485
  ).pipe(Command.withDescription("Fetch latest general PR discussion comment"));
408
486
 
409
487
  export const prCommentCommand = Command.make(
@@ -422,21 +500,25 @@ export const prCommentCommand = Command.make(
422
500
  Flag.withDescription("PR number (default: current branch PR)"),
423
501
  Flag.optional,
424
502
  ),
503
+ repo: repoOption,
425
504
  },
426
- ({ body, bodyFile, format, pr }) =>
427
- Effect.gen(function* () {
428
- const prNumber = Option.getOrNull(pr);
429
- const resolvedBody = yield* resolveRequiredTextInput(
430
- "gh-tool pr comment",
431
- Option.getOrNull(body),
432
- Option.getOrNull(bodyFile),
433
- "--body",
434
- "--body-file",
435
- "body",
436
- );
437
- const result = yield* postIssueComment(prNumber, resolvedBody);
438
- yield* logFormatted(result, format);
439
- }),
505
+ ({ body, bodyFile, format, pr, repo }) =>
506
+ withRepo(
507
+ repo,
508
+ Effect.gen(function* () {
509
+ const prNumber = Option.getOrNull(pr);
510
+ const resolvedBody = yield* resolveRequiredTextInput({
511
+ command: "gh-tool pr comment",
512
+ value: Option.getOrNull(body),
513
+ fileValue: Option.getOrNull(bodyFile),
514
+ valueFlag: "--body",
515
+ fileFlag: "--body-file",
516
+ label: "body",
517
+ });
518
+ const result = yield* postIssueComment(prNumber, resolvedBody);
519
+ yield* logFormatted(result, format);
520
+ }),
521
+ ),
440
522
  ).pipe(Command.withDescription("Post a general PR discussion comment"));
441
523
 
442
524
  export const prDiscussionSummaryCommand = Command.make(
@@ -447,13 +529,17 @@ export const prDiscussionSummaryCommand = Command.make(
447
529
  Flag.withDescription("PR number (default: current branch PR)"),
448
530
  Flag.optional,
449
531
  ),
532
+ repo: repoOption,
450
533
  },
451
- ({ format, pr }) =>
452
- Effect.gen(function* () {
453
- const prNumber = Option.getOrNull(pr);
454
- const summary = yield* fetchDiscussionSummary(prNumber);
455
- yield* logFormatted(summary, format);
456
- }),
534
+ ({ format, pr, repo }) =>
535
+ withRepo(
536
+ repo,
537
+ Effect.gen(function* () {
538
+ const prNumber = Option.getOrNull(pr);
539
+ const summary = yield* fetchDiscussionSummary(prNumber);
540
+ yield* logFormatted(summary, format);
541
+ }),
542
+ ),
457
543
  ).pipe(
458
544
  Command.withDescription("Fetch counts and latest comment across PR discussions and reviews"),
459
545
  );
@@ -474,21 +560,25 @@ export const prReplyCommand = Command.make(
474
560
  Flag.withDescription("PR number (default: current branch PR)"),
475
561
  Flag.optional,
476
562
  ),
563
+ repo: repoOption,
477
564
  },
478
- ({ body, bodyFile, commentId, format, pr }) =>
479
- Effect.gen(function* () {
480
- const prNumber = Option.getOrNull(pr);
481
- const resolvedBody = yield* resolveRequiredTextInput(
482
- "gh-tool pr reply",
483
- Option.getOrNull(body),
484
- Option.getOrNull(bodyFile),
485
- "--body",
486
- "--body-file",
487
- "body",
488
- );
489
- const result = yield* replyToComment(prNumber, commentId, resolvedBody);
490
- yield* logFormatted(result, format);
491
- }),
565
+ ({ body, bodyFile, commentId, format, pr, repo }) =>
566
+ withRepo(
567
+ repo,
568
+ Effect.gen(function* () {
569
+ const prNumber = Option.getOrNull(pr);
570
+ const resolvedBody = yield* resolveRequiredTextInput({
571
+ command: "gh-tool pr reply",
572
+ value: Option.getOrNull(body),
573
+ fileValue: Option.getOrNull(bodyFile),
574
+ valueFlag: "--body",
575
+ fileFlag: "--body-file",
576
+ label: "body",
577
+ });
578
+ const result = yield* replyToComment(prNumber, commentId, resolvedBody);
579
+ yield* logFormatted(result, format);
580
+ }),
581
+ ),
492
582
  ).pipe(Command.withDescription("Reply to an inline review comment"));
493
583
 
494
584
  export const prResolveCommand = Command.make(
@@ -498,12 +588,16 @@ export const prResolveCommand = Command.make(
498
588
  threadId: Flag.string("thread-id").pipe(
499
589
  Flag.withDescription("GraphQL node ID of the thread to resolve"),
500
590
  ),
591
+ repo: repoOption,
501
592
  },
502
- ({ format, threadId }) =>
503
- Effect.gen(function* () {
504
- const result = yield* resolveThread(threadId);
505
- yield* logFormatted(result, format);
506
- }),
593
+ ({ format, repo, threadId }) =>
594
+ withRepo(
595
+ repo,
596
+ Effect.gen(function* () {
597
+ const result = yield* resolveThread(threadId);
598
+ yield* logFormatted(result, format);
599
+ }),
600
+ ),
507
601
  ).pipe(Command.withDescription("Resolve a review thread via GraphQL"));
508
602
 
509
603
  export const prSubmitReviewCommand = Command.make(
@@ -522,6 +616,7 @@ export const prSubmitReviewCommand = Command.make(
522
616
  Flag.withDescription("PR number (default: current branch PR)"),
523
617
  Flag.optional,
524
618
  ),
619
+ repo: repoOption,
525
620
  reviewId: Flag.string("review-id").pipe(
526
621
  Flag.withDescription(
527
622
  "Pending review GraphQL ID (defaults to current user's pending review on PR)",
@@ -529,21 +624,24 @@ export const prSubmitReviewCommand = Command.make(
529
624
  Flag.optional,
530
625
  ),
531
626
  },
532
- ({ body, bodyFile, format, pr, reviewId }) =>
533
- Effect.gen(function* () {
534
- const prNumber = Option.getOrNull(pr);
535
- const reviewIdValue = Option.getOrNull(reviewId);
536
- const bodyValue = yield* resolveOptionalTextInput(
537
- "gh-tool pr submit-review",
538
- Option.getOrNull(body),
539
- Option.getOrNull(bodyFile),
540
- "--body",
541
- "--body-file",
542
- "body",
543
- );
544
- const result = yield* submitPendingReview(prNumber, reviewIdValue, bodyValue);
545
- yield* logFormatted(result, format);
546
- }),
627
+ ({ body, bodyFile, format, pr, repo, reviewId }) =>
628
+ withRepo(
629
+ repo,
630
+ Effect.gen(function* () {
631
+ const prNumber = Option.getOrNull(pr);
632
+ const reviewIdValue = Option.getOrNull(reviewId);
633
+ const bodyValue = yield* resolveOptionalTextInput({
634
+ command: "gh-tool pr submit-review",
635
+ value: Option.getOrNull(body),
636
+ fileValue: Option.getOrNull(bodyFile),
637
+ valueFlag: "--body",
638
+ fileFlag: "--body-file",
639
+ label: "body",
640
+ });
641
+ const result = yield* submitPendingReview(prNumber, reviewIdValue, bodyValue);
642
+ yield* logFormatted(result, format);
643
+ }),
644
+ ),
547
645
  ).pipe(
548
646
  Command.withDescription(
549
647
  "Submit a pending review as COMMENT (auto-detects your pending review if --review-id is omitted)",
@@ -558,19 +656,26 @@ export const prReviewTriageCommand = Command.make(
558
656
  Flag.withDescription("PR number (default: current branch PR)"),
559
657
  Flag.optional,
560
658
  ),
659
+ repo: repoOption,
561
660
  },
562
- ({ format, pr }) =>
563
- Effect.gen(function* () {
564
- const prNumber = Option.getOrNull(pr);
565
- const [info, unresolvedThreads, visibleOpenThreads, summary, checks] = yield* Effect.all([
566
- viewPR(prNumber),
567
- fetchThreads(prNumber, true),
568
- fetchThreads(prNumber, false, true),
569
- fetchDiscussionSummary(prNumber),
570
- fetchChecks(prNumber, false, false, 0),
571
- ]);
572
- yield* logFormatted({ info, unresolvedThreads, visibleOpenThreads, summary, checks }, format);
573
- }),
661
+ ({ format, pr, repo }) =>
662
+ withRepo(
663
+ repo,
664
+ Effect.gen(function* () {
665
+ const prNumber = Option.getOrNull(pr);
666
+ const [info, unresolvedThreads, visibleOpenThreads, summary, checks] = yield* Effect.all([
667
+ viewPR(prNumber),
668
+ fetchThreads(prNumber, true),
669
+ fetchThreads(prNumber, false, true),
670
+ fetchDiscussionSummary(prNumber),
671
+ fetchChecks(prNumber, false, false, 0),
672
+ ]);
673
+ yield* logFormatted(
674
+ { info, unresolvedThreads, visibleOpenThreads, summary, checks },
675
+ format,
676
+ );
677
+ }),
678
+ ),
574
679
  ).pipe(
575
680
  Command.withDescription(
576
681
  "Composite: PR info + unresolved threads + visible-open threads + discussion summary + checks status in one call",
@@ -593,25 +698,29 @@ export const prReplyAndResolveCommand = Command.make(
593
698
  Flag.withDescription("PR number (default: current branch PR)"),
594
699
  Flag.optional,
595
700
  ),
701
+ repo: repoOption,
596
702
  threadId: Flag.string("thread-id").pipe(
597
703
  Flag.withDescription("GraphQL node ID of the thread to resolve"),
598
704
  ),
599
705
  },
600
- ({ body, bodyFile, commentId, format, pr, threadId }) =>
601
- Effect.gen(function* () {
602
- const prNumber = Option.getOrNull(pr);
603
- const resolvedBody = yield* resolveRequiredTextInput(
604
- "gh-tool pr reply-and-resolve",
605
- Option.getOrNull(body),
606
- Option.getOrNull(bodyFile),
607
- "--body",
608
- "--body-file",
609
- "body",
610
- );
611
- const replyResult = yield* replyToComment(prNumber, commentId, resolvedBody);
612
- const resolveResult = yield* resolveThread(threadId);
613
- yield* logFormatted({ reply: replyResult, resolve: resolveResult }, format);
614
- }),
706
+ ({ body, bodyFile, commentId, format, pr, repo, threadId }) =>
707
+ withRepo(
708
+ repo,
709
+ Effect.gen(function* () {
710
+ const prNumber = Option.getOrNull(pr);
711
+ const resolvedBody = yield* resolveRequiredTextInput({
712
+ command: "gh-tool pr reply-and-resolve",
713
+ value: Option.getOrNull(body),
714
+ fileValue: Option.getOrNull(bodyFile),
715
+ valueFlag: "--body",
716
+ fileFlag: "--body-file",
717
+ label: "body",
718
+ });
719
+ const replyResult = yield* replyToComment(prNumber, commentId, resolvedBody);
720
+ const resolveResult = yield* resolveThread(threadId);
721
+ yield* logFormatted({ reply: replyResult, resolve: resolveResult }, format);
722
+ }),
723
+ ),
615
724
  ).pipe(
616
725
  Command.withDescription(
617
726
  "Composite: reply to a review comment and resolve its thread in one call",
@@ -416,14 +416,14 @@ export const releaseCreateCommand = Command.make(
416
416
  verifyTag,
417
417
  }) =>
418
418
  Effect.gen(function* () {
419
- const resolvedBody = yield* resolveOptionalTextInput(
420
- "gh-tool release create",
421
- Option.getOrNull(body),
422
- Option.getOrNull(bodyFile),
423
- "--body",
424
- "--body-file",
425
- "body",
426
- );
419
+ const resolvedBody = yield* resolveOptionalTextInput({
420
+ command: "gh-tool release create",
421
+ value: Option.getOrNull(body),
422
+ fileValue: Option.getOrNull(bodyFile),
423
+ valueFlag: "--body",
424
+ fileFlag: "--body-file",
425
+ label: "body",
426
+ });
427
427
 
428
428
  const result = yield* createRelease({
429
429
  tag,
@@ -526,14 +526,14 @@ export const releaseEditCommand = Command.make(
526
526
  },
527
527
  ({ body, bodyFile, draft, format, latest, prerelease, repo, tag, title }) =>
528
528
  Effect.gen(function* () {
529
- const resolvedBody = yield* resolveOptionalTextInput(
530
- "gh-tool release edit",
531
- Option.getOrNull(body),
532
- Option.getOrNull(bodyFile),
533
- "--body",
534
- "--body-file",
535
- "body",
536
- );
529
+ const resolvedBody = yield* resolveOptionalTextInput({
530
+ command: "gh-tool release edit",
531
+ value: Option.getOrNull(body),
532
+ fileValue: Option.getOrNull(bodyFile),
533
+ valueFlag: "--body",
534
+ fileFlag: "--body-file",
535
+ label: "body",
536
+ });
537
537
 
538
538
  const edited = yield* editRelease({
539
539
  tag,
@@ -5,7 +5,7 @@ import type { RepoInfo } from "./types";
5
5
 
6
6
  import { GH_BINARY } from "./config";
7
7
  import { GitHubAuthError, GitHubCommandError, GitHubNotFoundError } from "./errors";
8
- import { ConfigService, getGitHubConfig } from "#config";
8
+ import { ConfigService, resolveGitHubRepoTarget } from "#config";
9
9
 
10
10
  type GhResult = {
11
11
  stdout: string;
@@ -25,6 +25,10 @@ export class GitHubService extends Context.Service<
25
25
  variables: Record<string, string | number | null>,
26
26
  ) => Effect.Effect<unknown, GhError>;
27
27
  readonly getRepoInfo: () => Effect.Effect<RepoInfo, GhError>;
28
+ readonly withRepoTarget: <A, E, R>(
29
+ target: string | null,
30
+ effect: Effect.Effect<A, E, R>,
31
+ ) => Effect.Effect<A, E | GitHubCommandError, R>;
28
32
  }
29
33
  >()("@agent-tools/GitHubService") {
30
34
  static readonly layer = Layer.effect(
@@ -33,14 +37,49 @@ export class GitHubService extends Context.Service<
33
37
  Effect.gen(function* () {
34
38
  const executor = yield* ChildProcessSpawner.ChildProcessSpawner;
35
39
  const config = yield* ConfigService;
36
- const ghRepoConfig = getGitHubConfig(config);
37
- const ghRepo = ghRepoConfig ? `${ghRepoConfig.owner}/${ghRepoConfig.repo}` : undefined;
40
+ const initialRepoTarget = (() => {
41
+ try {
42
+ return resolveGitHubRepoTarget(config);
43
+ } catch {
44
+ return undefined;
45
+ }
46
+ })();
47
+ const RepoTarget = Context.Reference<string | undefined>(
48
+ "@agent-tools/GitHubService/RepoTarget",
49
+ {
50
+ defaultValue: () => initialRepoTarget,
51
+ },
52
+ );
53
+
54
+ const repoInfoCache = new Map<string | null, RepoInfo>();
55
+
56
+ const resolveRepoTarget = Effect.fn("GitHubService.resolveRepoTarget")(function* (
57
+ target: string | null,
58
+ ) {
59
+ const resolved = yield* Effect.try({
60
+ try: () => resolveGitHubRepoTarget(config, target),
61
+ catch: (error) =>
62
+ new GitHubCommandError({
63
+ message: error instanceof Error ? error.message : String(error),
64
+ command: "gh-tool --repo",
65
+ exitCode: 1,
66
+ stderr: error instanceof Error ? error.message : String(error),
67
+ }),
68
+ });
69
+
70
+ return resolved;
71
+ });
38
72
 
39
- let cachedRepoInfo: RepoInfo | null = null;
73
+ const withRepoTarget = <A, E, R>(target: string | null, effect: Effect.Effect<A, E, R>) =>
74
+ Effect.gen(function* () {
75
+ const resolved = yield* resolveRepoTarget(target);
76
+ return yield* effect.pipe(Effect.provideService(RepoTarget, resolved));
77
+ });
40
78
 
41
79
  const executeGh = (args: string[]) =>
42
80
  Effect.scoped(
43
81
  Effect.gen(function* () {
82
+ const ghRepo = yield* RepoTarget;
44
83
  const command = ChildProcess.make(GH_BINARY, args, {
45
84
  stdout: "pipe",
46
85
  stderr: "pipe",
@@ -177,7 +216,10 @@ export class GitHubService extends Context.Service<
177
216
  });
178
217
 
179
218
  const getRepoInfo = Effect.fn("GitHubService.getRepoInfo")(function* () {
180
- if (cachedRepoInfo !== null) {
219
+ const ghRepo = yield* RepoTarget;
220
+ const cacheKey = ghRepo ?? null;
221
+ const cachedRepoInfo = repoInfoCache.get(cacheKey);
222
+ if (cachedRepoInfo) {
181
223
  return cachedRepoInfo;
182
224
  }
183
225
 
@@ -195,11 +237,11 @@ export class GitHubService extends Context.Service<
195
237
  url: result.url,
196
238
  };
197
239
 
198
- cachedRepoInfo = repoInfo;
240
+ repoInfoCache.set(cacheKey, repoInfo);
199
241
  return repoInfo;
200
242
  });
201
243
 
202
- return { runGh, runGhJson, runGraphQL, getRepoInfo };
244
+ return { runGh, runGhJson, runGraphQL, getRepoInfo, withRepoTarget };
203
245
  }),
204
246
  ),
205
247
  );
@@ -28,8 +28,10 @@ type ResolveTextInputOptions = {
28
28
  command: string;
29
29
  value: string | null;
30
30
  fileValue: string | null;
31
+ stdin?: boolean;
31
32
  valueFlag: string;
32
33
  fileFlag: string;
34
+ stdinFlag?: string;
33
35
  missingMode: Schema.Schema.Type<typeof MissingMode>;
34
36
  missingValue?: string;
35
37
  label: string;
@@ -38,16 +40,33 @@ type ResolveTextInputOptions = {
38
40
  const resolveTextInputInternal = Effect.fn("gh.resolveTextInputInternal")(function* (
39
41
  options: ResolveTextInputOptions,
40
42
  ) {
41
- const { command, fileFlag, fileValue, label, missingMode, missingValue, value, valueFlag } =
42
- options;
43
+ const {
44
+ command,
45
+ fileFlag,
46
+ fileValue,
47
+ label,
48
+ missingMode,
49
+ missingValue,
50
+ stdin = false,
51
+ stdinFlag,
52
+ value,
53
+ valueFlag,
54
+ } = options;
55
+
56
+ const sourceFlags = [valueFlag, fileFlag, ...(stdinFlag ? [stdinFlag] : [])];
57
+ const sourceFlagList =
58
+ sourceFlags.length === 2
59
+ ? `${sourceFlags[0]} or ${sourceFlags[1]}`
60
+ : `${sourceFlags.slice(0, -1).join(", ")}, or ${sourceFlags.at(-1)}`;
43
61
 
44
- if (value !== null && fileValue !== null) {
62
+ const providedCount = [value !== null, fileValue !== null, stdin].filter(Boolean).length;
63
+ if (providedCount > 1) {
45
64
  return yield* Effect.fail(
46
65
  new GitHubCommandError({
47
66
  command,
48
67
  exitCode: 1,
49
- stderr: `Provide exactly one of ${valueFlag} or ${fileFlag}`,
50
- message: `Provide exactly one of ${valueFlag} or ${fileFlag}`,
68
+ stderr: `Provide exactly one of ${sourceFlagList}`,
69
+ message: `Provide exactly one of ${sourceFlagList}`,
51
70
  }),
52
71
  );
53
72
  }
@@ -71,6 +90,19 @@ const resolveTextInputInternal = Effect.fn("gh.resolveTextInputInternal")(functi
71
90
  });
72
91
  }
73
92
 
93
+ if (stdin) {
94
+ return yield* Effect.tryPromise({
95
+ try: () => readTextFromStdin(),
96
+ catch: (error) =>
97
+ new GitHubCommandError({
98
+ command,
99
+ exitCode: 1,
100
+ stderr: `Failed to read ${label} from stdin: ${error instanceof Error ? error.message : String(error)}`,
101
+ message: `Failed to read ${label} from stdin: ${error instanceof Error ? error.message : String(error)}`,
102
+ }),
103
+ });
104
+ }
105
+
74
106
  if (missingMode === "null") {
75
107
  return null;
76
108
  }
@@ -83,64 +115,35 @@ const resolveTextInputInternal = Effect.fn("gh.resolveTextInputInternal")(functi
83
115
  new GitHubCommandError({
84
116
  command,
85
117
  exitCode: 1,
86
- stderr: `Missing ${label}. Provide ${valueFlag} or ${fileFlag}`,
87
- message: `Missing ${label}. Provide ${valueFlag} or ${fileFlag}`,
118
+ stderr: `Missing ${label}. Provide ${sourceFlagList}`,
119
+ message: `Missing ${label}. Provide ${sourceFlagList}`,
88
120
  }),
89
121
  );
90
122
  });
91
123
 
92
124
  export const resolveRequiredTextInput = (
93
- command: string,
94
- value: string | null,
95
- fileValue: string | null,
96
- valueFlag: string,
97
- fileFlag: string,
98
- label: string,
125
+ options: Omit<ResolveTextInputOptions, "missingMode" | "missingValue">,
99
126
  ): Effect.Effect<string, GitHubCommandError> =>
100
127
  resolveTextInputInternal({
101
- command,
102
- value,
103
- fileValue,
104
- valueFlag,
105
- fileFlag,
128
+ ...options,
106
129
  missingMode: "error",
107
- label,
108
130
  }).pipe(Effect.map((resolvedValue) => ensureResolvedText(resolvedValue, "required text input")));
109
131
 
110
132
  export const resolveOptionalTextInput = (
111
- command: string,
112
- value: string | null,
113
- fileValue: string | null,
114
- valueFlag: string,
115
- fileFlag: string,
116
- label: string,
133
+ options: Omit<ResolveTextInputOptions, "missingMode" | "missingValue">,
117
134
  ): Effect.Effect<string | null, GitHubCommandError> =>
118
135
  resolveTextInputInternal({
119
- command,
120
- value,
121
- fileValue,
122
- valueFlag,
123
- fileFlag,
136
+ ...options,
124
137
  missingMode: "null",
125
- label,
126
138
  });
127
139
 
128
140
  export const resolveDefaultTextInput = (
129
- command: string,
130
- value: string | null,
131
- fileValue: string | null,
132
- valueFlag: string,
133
- fileFlag: string,
134
- label: string,
135
- defaultValue: string,
141
+ options: Omit<ResolveTextInputOptions, "missingMode" | "missingValue"> & {
142
+ defaultValue: string;
143
+ },
136
144
  ): Effect.Effect<string, GitHubCommandError> =>
137
145
  resolveTextInputInternal({
138
- command,
139
- value,
140
- fileValue,
141
- valueFlag,
142
- fileFlag,
146
+ ...options,
143
147
  missingMode: "default",
144
- missingValue: defaultValue,
145
- label,
148
+ missingValue: options.defaultValue,
146
149
  }).pipe(Effect.map((resolvedValue) => ensureResolvedText(resolvedValue, "default text input")));