@blogic-cz/agent-tools 0.8.14 → 0.8.15

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.8.14",
3
+ "version": "0.8.15",
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",
@@ -112,8 +112,10 @@
112
112
  "check:ci": "bun check.ts ci",
113
113
  "format": "oxfmt",
114
114
  "format:check": "oxfmt --check",
115
+ "gh-tool": "bun src/gh-tool/index.ts",
115
116
  "lint": "oxlint -c ./.oxlintrc.json --deny-warnings",
116
117
  "lint:fix": "oxlint -c ./.oxlintrc.json --fix",
118
+ "session-tool": "bun src/session-tool/index.ts",
117
119
  "update:skills": "bun run .agents/skills/update-packages/references/skills-update-local.ts",
118
120
  "test": "vitest run"
119
121
  },
@@ -2,6 +2,7 @@ import { Command, Flag } from "effect/unstable/cli";
2
2
  import { Effect, Option } from "effect";
3
3
 
4
4
  import { formatOption, logFormatted } from "#shared";
5
+ import { resolveOptionalTextInput, resolveRequiredTextInput } from "#gh/text-input";
5
6
 
6
7
  import {
7
8
  closeIssue,
@@ -91,6 +92,10 @@ export const issueCloseCommand = Command.make(
91
92
  Flag.withDescription("Comment to add when closing"),
92
93
  Flag.optional,
93
94
  ),
95
+ commentFile: Flag.string("comment-file").pipe(
96
+ Flag.withDescription("Read close comment from a file path or '-' for stdin"),
97
+ Flag.optional,
98
+ ),
94
99
  format: formatOption,
95
100
  issue: Flag.integer("issue").pipe(Flag.withDescription("Issue number to close")),
96
101
  reason: Flag.choice("reason", ["completed", "not planned"]).pipe(
@@ -98,10 +103,19 @@ export const issueCloseCommand = Command.make(
98
103
  Flag.withDefault("completed"),
99
104
  ),
100
105
  },
101
- ({ comment, format, issue, reason }) =>
106
+ ({ comment, commentFile, format, issue, reason }) =>
102
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
+ );
116
+
103
117
  const result = yield* closeIssue({
104
- comment: Option.getOrNull(comment),
118
+ comment: resolvedComment,
105
119
  issue,
106
120
  reason,
107
121
  });
@@ -116,13 +130,26 @@ export const issueReopenCommand = Command.make(
116
130
  Flag.withDescription("Comment to add when reopening"),
117
131
  Flag.optional,
118
132
  ),
133
+ commentFile: Flag.string("comment-file").pipe(
134
+ Flag.withDescription("Read reopen comment from a file path or '-' for stdin"),
135
+ Flag.optional,
136
+ ),
119
137
  format: formatOption,
120
138
  issue: Flag.integer("issue").pipe(Flag.withDescription("Issue number to reopen")),
121
139
  },
122
- ({ comment, format, issue }) =>
140
+ ({ comment, commentFile, format, issue }) =>
123
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
+ );
150
+
124
151
  const result = yield* reopenIssue({
125
- comment: Option.getOrNull(comment),
152
+ comment: resolvedComment,
126
153
  issue,
127
154
  });
128
155
  yield* logFormatted(result, format);
@@ -132,13 +159,26 @@ export const issueReopenCommand = Command.make(
132
159
  export const issueCommentCommand = Command.make(
133
160
  "comment",
134
161
  {
135
- body: Flag.string("body").pipe(Flag.withDescription("Comment body text")),
162
+ body: Flag.string("body").pipe(Flag.withDescription("Comment body text"), Flag.optional),
163
+ bodyFile: Flag.string("body-file").pipe(
164
+ Flag.withDescription("Read comment body from a file path or '-' for stdin"),
165
+ Flag.optional,
166
+ ),
136
167
  format: formatOption,
137
168
  issue: Flag.integer("issue").pipe(Flag.withDescription("Issue number to comment on")),
138
169
  },
139
- ({ body, format, issue }) =>
170
+ ({ body, bodyFile, format, issue }) =>
140
171
  Effect.gen(function* () {
141
- const result = yield* commentOnIssue({ body, issue });
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
+ );
180
+
181
+ const result = yield* commentOnIssue({ body: resolvedBody, issue });
142
182
  yield* logFormatted(result, format);
143
183
  }),
144
184
  ).pipe(Command.withDescription("Post a comment on an issue"));
@@ -155,6 +195,10 @@ export const issueEditCommand = Command.make(
155
195
  Flag.optional,
156
196
  ),
157
197
  body: Flag.string("body").pipe(Flag.withDescription("New issue body"), Flag.optional),
198
+ bodyFile: Flag.string("body-file").pipe(
199
+ Flag.withDescription("Read issue body from a file path or '-' for stdin"),
200
+ Flag.optional,
201
+ ),
158
202
  format: formatOption,
159
203
  issue: Flag.integer("issue").pipe(Flag.withDescription("Issue number to edit")),
160
204
  removeAssignee: Flag.string("remove-assignee").pipe(
@@ -167,12 +211,31 @@ export const issueEditCommand = Command.make(
167
211
  ),
168
212
  title: Flag.string("title").pipe(Flag.withDescription("New issue title"), Flag.optional),
169
213
  },
170
- ({ addAssignee, addLabels, body, format, issue, removeAssignee, removeLabels, title }) =>
214
+ ({
215
+ addAssignee,
216
+ addLabels,
217
+ body,
218
+ bodyFile,
219
+ format,
220
+ issue,
221
+ removeAssignee,
222
+ removeLabels,
223
+ title,
224
+ }) =>
171
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
+ );
234
+
172
235
  const result = yield* editIssue({
173
236
  addAssignee: Option.getOrNull(addAssignee),
174
237
  addLabels: Option.getOrNull(addLabels),
175
- body: Option.getOrNull(body),
238
+ body: resolvedBody,
176
239
  issue,
177
240
  removeAssignee: Option.getOrNull(removeAssignee),
178
241
  removeLabels: Option.getOrNull(removeLabels),
@@ -4,6 +4,11 @@ import { Effect, Option } from "effect";
4
4
  import type { PRStatusResult } from "#gh/types";
5
5
 
6
6
  import { formatOption, logFormatted } from "#shared";
7
+ import {
8
+ resolveDefaultTextInput,
9
+ resolveOptionalTextInput,
10
+ resolveRequiredTextInput,
11
+ } from "#gh/text-input";
7
12
  import {
8
13
  CI_CHECK_WATCH_TIMEOUT_MS,
9
14
  DEFAULT_DELETE_BRANCH,
@@ -70,9 +75,10 @@ export const prCreateCommand = Command.make(
70
75
  Flag.withDescription("Base branch for the PR"),
71
76
  Flag.withDefault("test"),
72
77
  ),
73
- body: Flag.string("body").pipe(
74
- Flag.withDescription("PR body/description"),
75
- Flag.withDefault(""),
78
+ body: Flag.string("body").pipe(Flag.withDescription("PR body/description"), Flag.optional),
79
+ bodyFile: Flag.string("body-file").pipe(
80
+ Flag.withDescription("Read PR body from a file path or '-' for stdin"),
81
+ Flag.optional,
76
82
  ),
77
83
  draft: Flag.boolean("draft").pipe(
78
84
  Flag.withDescription("Create as draft PR"),
@@ -85,11 +91,21 @@ export const prCreateCommand = Command.make(
85
91
  ),
86
92
  title: Flag.string("title").pipe(Flag.withDescription("PR title")),
87
93
  },
88
- ({ base, body, draft, format, head, title }) =>
94
+ ({ base, body, bodyFile, draft, format, head, title }) =>
89
95
  Effect.gen(function* () {
96
+ const resolvedBody = yield* resolveDefaultTextInput(
97
+ "gh-tool pr create",
98
+ Option.getOrNull(body),
99
+ Option.getOrNull(bodyFile),
100
+ "--body",
101
+ "--body-file",
102
+ "body",
103
+ "",
104
+ );
105
+
90
106
  const info = yield* createPR({
91
107
  base,
92
- body,
108
+ body: resolvedBody,
93
109
  draft,
94
110
  head: Option.getOrNull(head),
95
111
  title,
@@ -102,16 +118,29 @@ export const prEditCommand = Command.make(
102
118
  "edit",
103
119
  {
104
120
  body: Flag.string("body").pipe(Flag.withDescription("New PR body/description"), Flag.optional),
121
+ bodyFile: Flag.string("body-file").pipe(
122
+ Flag.withDescription("Read PR body from a file path or '-' for stdin"),
123
+ Flag.optional,
124
+ ),
105
125
  format: formatOption,
106
126
  pr: Flag.integer("pr").pipe(Flag.withDescription("PR number to edit")),
107
127
  title: Flag.string("title").pipe(Flag.withDescription("New PR title"), Flag.optional),
108
128
  },
109
- ({ body, format, pr, title }) =>
129
+ ({ body, bodyFile, format, pr, title }) =>
110
130
  Effect.gen(function* () {
131
+ const resolvedBody = yield* resolveOptionalTextInput(
132
+ "gh-tool pr edit",
133
+ Option.getOrNull(body),
134
+ Option.getOrNull(bodyFile),
135
+ "--body",
136
+ "--body-file",
137
+ "body",
138
+ );
139
+
111
140
  const info = yield* editPR({
112
141
  pr,
113
142
  title: Option.getOrNull(title),
114
- body: Option.getOrNull(body),
143
+ body: resolvedBody,
115
144
  });
116
145
  yield* logFormatted(info, format);
117
146
  }),
@@ -338,17 +367,32 @@ export const prIssueCommentsLatestCommand = Command.make(
338
367
  export const prCommentCommand = Command.make(
339
368
  "comment",
340
369
  {
341
- body: Flag.string("body").pipe(Flag.withDescription("General PR comment body text")),
370
+ body: Flag.string("body").pipe(
371
+ Flag.withDescription("General PR comment body text"),
372
+ Flag.optional,
373
+ ),
374
+ bodyFile: Flag.string("body-file").pipe(
375
+ Flag.withDescription("Read general PR comment body from a file path or '-' for stdin"),
376
+ Flag.optional,
377
+ ),
342
378
  format: formatOption,
343
379
  pr: Flag.integer("pr").pipe(
344
380
  Flag.withDescription("PR number (default: current branch PR)"),
345
381
  Flag.optional,
346
382
  ),
347
383
  },
348
- ({ body, format, pr }) =>
384
+ ({ body, bodyFile, format, pr }) =>
349
385
  Effect.gen(function* () {
350
386
  const prNumber = Option.getOrNull(pr);
351
- const result = yield* postIssueComment(prNumber, body);
387
+ const resolvedBody = yield* resolveRequiredTextInput(
388
+ "gh-tool pr comment",
389
+ Option.getOrNull(body),
390
+ Option.getOrNull(bodyFile),
391
+ "--body",
392
+ "--body-file",
393
+ "body",
394
+ );
395
+ const result = yield* postIssueComment(prNumber, resolvedBody);
352
396
  yield* logFormatted(result, format);
353
397
  }),
354
398
  ).pipe(Command.withDescription("Post a general PR discussion comment"));
@@ -375,7 +419,11 @@ export const prDiscussionSummaryCommand = Command.make(
375
419
  export const prReplyCommand = Command.make(
376
420
  "reply",
377
421
  {
378
- body: Flag.string("body").pipe(Flag.withDescription("Reply body text")),
422
+ body: Flag.string("body").pipe(Flag.withDescription("Reply body text"), Flag.optional),
423
+ bodyFile: Flag.string("body-file").pipe(
424
+ Flag.withDescription("Read reply body from a file path or '-' for stdin"),
425
+ Flag.optional,
426
+ ),
379
427
  commentId: Flag.integer("comment-id").pipe(
380
428
  Flag.withDescription("ID of the comment to reply to"),
381
429
  ),
@@ -385,10 +433,18 @@ export const prReplyCommand = Command.make(
385
433
  Flag.optional,
386
434
  ),
387
435
  },
388
- ({ body, commentId, format, pr }) =>
436
+ ({ body, bodyFile, commentId, format, pr }) =>
389
437
  Effect.gen(function* () {
390
438
  const prNumber = Option.getOrNull(pr);
391
- const result = yield* replyToComment(prNumber, commentId, body);
439
+ const resolvedBody = yield* resolveRequiredTextInput(
440
+ "gh-tool pr reply",
441
+ Option.getOrNull(body),
442
+ Option.getOrNull(bodyFile),
443
+ "--body",
444
+ "--body-file",
445
+ "body",
446
+ );
447
+ const result = yield* replyToComment(prNumber, commentId, resolvedBody);
392
448
  yield* logFormatted(result, format);
393
449
  }),
394
450
  ).pipe(Command.withDescription("Reply to an inline review comment"));
@@ -415,6 +471,10 @@ export const prSubmitReviewCommand = Command.make(
415
471
  Flag.withDescription("Optional review body text when submitting"),
416
472
  Flag.optional,
417
473
  ),
474
+ bodyFile: Flag.string("body-file").pipe(
475
+ Flag.withDescription("Read review body from a file path or '-' for stdin"),
476
+ Flag.optional,
477
+ ),
418
478
  format: formatOption,
419
479
  pr: Flag.integer("pr").pipe(
420
480
  Flag.withDescription("PR number (default: current branch PR)"),
@@ -427,11 +487,18 @@ export const prSubmitReviewCommand = Command.make(
427
487
  Flag.optional,
428
488
  ),
429
489
  },
430
- ({ body, format, pr, reviewId }) =>
490
+ ({ body, bodyFile, format, pr, reviewId }) =>
431
491
  Effect.gen(function* () {
432
492
  const prNumber = Option.getOrNull(pr);
433
493
  const reviewIdValue = Option.getOrNull(reviewId);
434
- const bodyValue = Option.getOrNull(body);
494
+ const bodyValue = yield* resolveOptionalTextInput(
495
+ "gh-tool pr submit-review",
496
+ Option.getOrNull(body),
497
+ Option.getOrNull(bodyFile),
498
+ "--body",
499
+ "--body-file",
500
+ "body",
501
+ );
435
502
  const result = yield* submitPendingReview(prNumber, reviewIdValue, bodyValue);
436
503
  yield* logFormatted(result, format);
437
504
  }),
@@ -471,7 +538,11 @@ export const prReviewTriageCommand = Command.make(
471
538
  export const prReplyAndResolveCommand = Command.make(
472
539
  "reply-and-resolve",
473
540
  {
474
- body: Flag.string("body").pipe(Flag.withDescription("Reply body text")),
541
+ body: Flag.string("body").pipe(Flag.withDescription("Reply body text"), Flag.optional),
542
+ bodyFile: Flag.string("body-file").pipe(
543
+ Flag.withDescription("Read reply body from a file path or '-' for stdin"),
544
+ Flag.optional,
545
+ ),
475
546
  commentId: Flag.integer("comment-id").pipe(
476
547
  Flag.withDescription("ID of the comment to reply to"),
477
548
  ),
@@ -484,10 +555,18 @@ export const prReplyAndResolveCommand = Command.make(
484
555
  Flag.withDescription("GraphQL node ID of the thread to resolve"),
485
556
  ),
486
557
  },
487
- ({ body, commentId, format, pr, threadId }) =>
558
+ ({ body, bodyFile, commentId, format, pr, threadId }) =>
488
559
  Effect.gen(function* () {
489
560
  const prNumber = Option.getOrNull(pr);
490
- const replyResult = yield* replyToComment(prNumber, commentId, body);
561
+ const resolvedBody = yield* resolveRequiredTextInput(
562
+ "gh-tool pr reply-and-resolve",
563
+ Option.getOrNull(body),
564
+ Option.getOrNull(bodyFile),
565
+ "--body",
566
+ "--body-file",
567
+ "body",
568
+ );
569
+ const replyResult = yield* replyToComment(prNumber, commentId, resolvedBody);
491
570
  const resolveResult = yield* resolveThread(threadId);
492
571
  yield* logFormatted({ reply: replyResult, resolve: resolveResult }, format);
493
572
  }),
@@ -2,6 +2,7 @@ import { Command, Flag } from "effect/unstable/cli";
2
2
  import { Effect, Option } from "effect";
3
3
 
4
4
  import { formatOption, logFormatted } from "#shared";
5
+ import { resolveOptionalTextInput } from "#gh/text-input";
5
6
  import { GitHubService } from "./service";
6
7
 
7
8
  type ReleaseListItem = {
@@ -343,6 +344,10 @@ export const releaseCreateCommand = Command.make(
343
344
  Flag.withDescription("Release notes body (markdown)"),
344
345
  Flag.optional,
345
346
  ),
347
+ bodyFile: Flag.string("body-file").pipe(
348
+ Flag.withDescription("Read release notes body from a file path or '-' for stdin"),
349
+ Flag.optional,
350
+ ),
346
351
  draft: Flag.boolean("draft").pipe(
347
352
  Flag.withDescription("Create as draft release"),
348
353
  Flag.withDefault(false),
@@ -388,6 +393,7 @@ export const releaseCreateCommand = Command.make(
388
393
  },
389
394
  ({
390
395
  body,
396
+ bodyFile,
391
397
  draft,
392
398
  format,
393
399
  generateNotes,
@@ -402,10 +408,19 @@ export const releaseCreateCommand = Command.make(
402
408
  verifyTag,
403
409
  }) =>
404
410
  Effect.gen(function* () {
411
+ const resolvedBody = yield* resolveOptionalTextInput(
412
+ "gh-tool release create",
413
+ Option.getOrNull(body),
414
+ Option.getOrNull(bodyFile),
415
+ "--body",
416
+ "--body-file",
417
+ "body",
418
+ );
419
+
405
420
  const result = yield* createRelease({
406
421
  tag,
407
422
  title: Option.getOrNull(title),
408
- body: Option.getOrNull(body),
423
+ body: resolvedBody,
409
424
  notesFile: Option.getOrNull(notesFile),
410
425
  draft,
411
426
  prerelease,
@@ -477,6 +492,10 @@ export const releaseEditCommand = Command.make(
477
492
  Flag.withDescription("New release notes body (markdown)"),
478
493
  Flag.optional,
479
494
  ),
495
+ bodyFile: Flag.string("body-file").pipe(
496
+ Flag.withDescription("Read release notes body from a file path or '-' for stdin"),
497
+ Flag.optional,
498
+ ),
480
499
  draft: Flag.boolean("draft").pipe(
481
500
  Flag.withDescription("Set draft status (true/false). Omit to keep current value"),
482
501
  Flag.optional,
@@ -497,12 +516,21 @@ export const releaseEditCommand = Command.make(
497
516
  tag: Flag.string("tag").pipe(Flag.withDescription("Release tag to edit (e.g., v1.2.3)")),
498
517
  title: Flag.string("title").pipe(Flag.withDescription("New release title"), Flag.optional),
499
518
  },
500
- ({ body, draft, format, latest, prerelease, repo, tag, title }) =>
519
+ ({ body, bodyFile, draft, format, latest, prerelease, repo, tag, title }) =>
501
520
  Effect.gen(function* () {
521
+ const resolvedBody = yield* resolveOptionalTextInput(
522
+ "gh-tool release edit",
523
+ Option.getOrNull(body),
524
+ Option.getOrNull(bodyFile),
525
+ "--body",
526
+ "--body-file",
527
+ "body",
528
+ );
529
+
502
530
  const edited = yield* editRelease({
503
531
  tag,
504
532
  title: Option.getOrNull(title),
505
- body: Option.getOrNull(body),
533
+ body: resolvedBody,
506
534
  draft: Option.getOrNull(draft),
507
535
  prerelease: Option.getOrNull(prerelease),
508
536
  latest: Option.getOrNull(latest),
@@ -0,0 +1,146 @@
1
+ import { Effect, Schema } from "effect";
2
+
3
+ import { GitHubCommandError } from "#gh/errors";
4
+
5
+ const STDIN_SENTINEL = "-";
6
+ const SENSITIVE_PATH_PATTERNS = [/\.env(\..+)?$/, /\.envrc$/, /\.(pem|key|p12|pfx|cer|crt)$/i];
7
+ const MissingMode = Schema.Literals(["error", "null", "default"]);
8
+
9
+ const readTextFromStdin = () => Bun.stdin.text();
10
+
11
+ const readTextFile = (filePath: string) => {
12
+ if (SENSITIVE_PATH_PATTERNS.some((pattern) => pattern.test(filePath))) {
13
+ return Promise.reject(new Error(`Refusing to read sensitive file: ${filePath}`));
14
+ }
15
+
16
+ return Bun.file(filePath).text();
17
+ };
18
+
19
+ const ensureResolvedText = (resolvedValue: string | null, context: string) => {
20
+ if (resolvedValue === null) {
21
+ throw new Error(`Invariant violation: ${context} resolved to null`);
22
+ }
23
+
24
+ return resolvedValue;
25
+ };
26
+
27
+ type ResolveTextInputOptions = {
28
+ command: string;
29
+ value: string | null;
30
+ fileValue: string | null;
31
+ valueFlag: string;
32
+ fileFlag: string;
33
+ missingMode: Schema.Schema.Type<typeof MissingMode>;
34
+ missingValue?: string;
35
+ label: string;
36
+ };
37
+
38
+ const resolveTextInputInternal = Effect.fn("gh.resolveTextInputInternal")(function* (
39
+ options: ResolveTextInputOptions,
40
+ ) {
41
+ const { command, fileFlag, fileValue, label, missingMode, missingValue, value, valueFlag } =
42
+ options;
43
+
44
+ if (value !== null && fileValue !== null) {
45
+ return yield* Effect.fail(
46
+ new GitHubCommandError({
47
+ command,
48
+ exitCode: 1,
49
+ stderr: `Provide exactly one of ${valueFlag} or ${fileFlag}`,
50
+ message: `Provide exactly one of ${valueFlag} or ${fileFlag}`,
51
+ }),
52
+ );
53
+ }
54
+
55
+ if (value !== null) {
56
+ return value;
57
+ }
58
+
59
+ if (fileValue !== null) {
60
+ const source = fileValue === STDIN_SENTINEL ? "stdin" : fileValue;
61
+
62
+ return yield* Effect.tryPromise({
63
+ try: () => (fileValue === STDIN_SENTINEL ? readTextFromStdin() : readTextFile(fileValue)),
64
+ catch: (error) =>
65
+ new GitHubCommandError({
66
+ command,
67
+ exitCode: 1,
68
+ stderr: `Failed to read ${label} from ${source}: ${error instanceof Error ? error.message : String(error)}`,
69
+ message: `Failed to read ${label} from ${source}: ${error instanceof Error ? error.message : String(error)}`,
70
+ }),
71
+ });
72
+ }
73
+
74
+ if (missingMode === "null") {
75
+ return null;
76
+ }
77
+
78
+ if (missingMode === "default") {
79
+ return missingValue ?? "";
80
+ }
81
+
82
+ return yield* Effect.fail(
83
+ new GitHubCommandError({
84
+ command,
85
+ exitCode: 1,
86
+ stderr: `Missing ${label}. Provide ${valueFlag} or ${fileFlag}`,
87
+ message: `Missing ${label}. Provide ${valueFlag} or ${fileFlag}`,
88
+ }),
89
+ );
90
+ });
91
+
92
+ export const resolveRequiredTextInput = (
93
+ command: string,
94
+ value: string | null,
95
+ fileValue: string | null,
96
+ valueFlag: string,
97
+ fileFlag: string,
98
+ label: string,
99
+ ): Effect.Effect<string, GitHubCommandError> =>
100
+ resolveTextInputInternal({
101
+ command,
102
+ value,
103
+ fileValue,
104
+ valueFlag,
105
+ fileFlag,
106
+ missingMode: "error",
107
+ label,
108
+ }).pipe(Effect.map((resolvedValue) => ensureResolvedText(resolvedValue, "required text input")));
109
+
110
+ export const resolveOptionalTextInput = (
111
+ command: string,
112
+ value: string | null,
113
+ fileValue: string | null,
114
+ valueFlag: string,
115
+ fileFlag: string,
116
+ label: string,
117
+ ): Effect.Effect<string | null, GitHubCommandError> =>
118
+ resolveTextInputInternal({
119
+ command,
120
+ value,
121
+ fileValue,
122
+ valueFlag,
123
+ fileFlag,
124
+ missingMode: "null",
125
+ label,
126
+ });
127
+
128
+ 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,
136
+ ): Effect.Effect<string, GitHubCommandError> =>
137
+ resolveTextInputInternal({
138
+ command,
139
+ value,
140
+ fileValue,
141
+ valueFlag,
142
+ fileFlag,
143
+ missingMode: "default",
144
+ missingValue: defaultValue,
145
+ label,
146
+ }).pipe(Effect.map((resolvedValue) => ensureResolvedText(resolvedValue, "default text input")));