@blogic-cz/agent-tools 1.11.1 → 1.13.0

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.
Files changed (100) hide show
  1. package/README.md +18 -43
  2. package/dist/credential-guard/bash.d.ts +3 -0
  3. package/dist/credential-guard/bash.d.ts.map +1 -0
  4. package/dist/credential-guard/claude-hook.d.ts.map +1 -1
  5. package/dist/credential-guard/engine.test.d.ts +5 -0
  6. package/dist/credential-guard/engine.test.d.ts.map +1 -0
  7. package/dist/credential-guard/index.d.ts +8 -35
  8. package/dist/credential-guard/index.d.ts.map +1 -1
  9. package/dist/credential-guard/ir.d.ts +150 -0
  10. package/dist/credential-guard/ir.d.ts.map +1 -0
  11. package/dist/credential-guard/literal-output.d.ts +2 -0
  12. package/dist/credential-guard/literal-output.d.ts.map +1 -0
  13. package/dist/credential-guard/passive-awk.d.ts +2 -0
  14. package/dist/credential-guard/passive-awk.d.ts.map +1 -0
  15. package/dist/credential-guard/passive-jq.d.ts.map +1 -1
  16. package/dist/credential-guard/passive-sed.d.ts +2 -0
  17. package/dist/credential-guard/passive-sed.d.ts.map +1 -0
  18. package/dist/credential-guard/passive.test.d.ts +2 -0
  19. package/dist/credential-guard/passive.test.d.ts.map +1 -0
  20. package/dist/credential-guard/paths.d.ts +14 -0
  21. package/dist/credential-guard/paths.d.ts.map +1 -0
  22. package/dist/credential-guard/policy.d.ts +47 -0
  23. package/dist/credential-guard/policy.d.ts.map +1 -0
  24. package/dist/credential-guard/powershell.d.ts +3 -0
  25. package/dist/credential-guard/powershell.d.ts.map +1 -0
  26. package/dist/credential-guard/registry.d.ts +89 -0
  27. package/dist/credential-guard/registry.d.ts.map +1 -0
  28. package/dist/credential-guard/review4.test.d.ts +2 -0
  29. package/dist/credential-guard/review4.test.d.ts.map +1 -0
  30. package/dist/credential-guard/review7.test.d.ts +2 -0
  31. package/dist/credential-guard/review7.test.d.ts.map +1 -0
  32. package/dist/credential-guard/shell-oracle.d.ts +2 -0
  33. package/dist/credential-guard/shell-oracle.d.ts.map +1 -0
  34. package/dist/credential-guard/xargs.d.ts +7 -0
  35. package/dist/credential-guard/xargs.d.ts.map +1 -0
  36. package/dist/gh-tool/pr/checkbox-markdown.d.ts +22 -0
  37. package/dist/gh-tool/pr/checkbox-markdown.d.ts.map +1 -0
  38. package/dist/gh-tool/pr/checkbox.d.ts +21 -0
  39. package/dist/gh-tool/pr/checkbox.d.ts.map +1 -0
  40. package/dist/gh-tool/pr/commands.d.ts +9 -0
  41. package/dist/gh-tool/pr/commands.d.ts.map +1 -1
  42. package/dist/gh-tool/pr/index.d.ts +1 -1
  43. package/dist/gh-tool/pr/index.d.ts.map +1 -1
  44. package/dist/gh-tool/read/api.d.ts +8 -0
  45. package/dist/gh-tool/read/api.d.ts.map +1 -1
  46. package/dist/gh-tool/read/cache.d.ts.map +1 -1
  47. package/dist/gh-tool/read/commands.d.ts +5 -0
  48. package/dist/gh-tool/read/commands.d.ts.map +1 -1
  49. package/dist/gh-tool/read/pulls.d.ts +1 -0
  50. package/dist/gh-tool/read/pulls.d.ts.map +1 -1
  51. package/dist/gh-tool/read/reads.d.ts +39 -0
  52. package/dist/gh-tool/read/reads.d.ts.map +1 -1
  53. package/dist/gh-tool/read/telemetry-command.d.ts +7 -0
  54. package/dist/gh-tool/read/telemetry-command.d.ts.map +1 -0
  55. package/dist/gh-tool/read/telemetry-store.d.ts +3 -0
  56. package/dist/gh-tool/read/telemetry-store.d.ts.map +1 -0
  57. package/dist/gh-tool/read/telemetry.d.ts +59 -0
  58. package/dist/gh-tool/read/telemetry.d.ts.map +1 -0
  59. package/dist/gh-tool/read/transport.d.ts +9 -0
  60. package/dist/gh-tool/read/transport.d.ts.map +1 -1
  61. package/dist/gh-tool/read/writes.d.ts +2 -1
  62. package/dist/gh-tool/read/writes.d.ts.map +1 -1
  63. package/package.json +2 -1
  64. package/schemas/agent-tools.schema.json +1 -1
  65. package/src/credential-guard/bash.ts +769 -0
  66. package/src/credential-guard/claude-hook.ts +14 -14
  67. package/src/credential-guard/engine.test.ts +2050 -0
  68. package/src/credential-guard/index.ts +285 -4268
  69. package/src/credential-guard/ir.ts +139 -0
  70. package/src/credential-guard/literal-output.ts +8 -0
  71. package/src/credential-guard/passive-awk.ts +239 -0
  72. package/src/credential-guard/passive-jq.ts +183 -24
  73. package/src/credential-guard/passive-sed.ts +78 -0
  74. package/src/credential-guard/passive.test.ts +141 -0
  75. package/src/credential-guard/paths.ts +108 -0
  76. package/src/credential-guard/policy.ts +1576 -0
  77. package/src/credential-guard/powershell.ts +220 -0
  78. package/src/credential-guard/registry.ts +4720 -0
  79. package/src/credential-guard/review4.test.ts +83 -0
  80. package/src/credential-guard/review7.test.ts +51 -0
  81. package/src/credential-guard/rg-types.json +389 -0
  82. package/src/credential-guard/shell-oracle.ts +187 -0
  83. package/src/credential-guard/xargs.ts +65 -0
  84. package/src/gh-tool/main.ts +32 -6
  85. package/src/gh-tool/pr/checkbox-markdown.ts +317 -0
  86. package/src/gh-tool/pr/checkbox.ts +113 -0
  87. package/src/gh-tool/pr/commands.ts +55 -2
  88. package/src/gh-tool/pr/core.ts +2 -2
  89. package/src/gh-tool/pr/index.ts +1 -0
  90. package/src/gh-tool/read/api.ts +73 -6
  91. package/src/gh-tool/read/cache.ts +13 -4
  92. package/src/gh-tool/read/commands.ts +17 -1
  93. package/src/gh-tool/read/pulls.ts +19 -0
  94. package/src/gh-tool/read/reads.ts +86 -2
  95. package/src/gh-tool/read/telemetry-command.ts +45 -0
  96. package/src/gh-tool/read/telemetry-store.ts +85 -0
  97. package/src/gh-tool/read/telemetry.ts +191 -0
  98. package/src/gh-tool/read/transport.ts +95 -6
  99. package/src/gh-tool/read/writes.ts +19 -2
  100. package/src/gh-tool/workflow.ts +2 -2
@@ -0,0 +1,191 @@
1
+ import { Context, Effect } from "effect";
2
+ import { redactSensitiveText } from "#shared/content-security";
3
+
4
+ export type RequestEvent = {
5
+ ts: string;
6
+ method: string;
7
+ endpoint: string;
8
+ status: number | null;
9
+ outcome: "cache" | "304" | "200" | "bypass" | "error" | "subprocess";
10
+ resource: string | null;
11
+ used: number | null;
12
+ remaining: number | null;
13
+ reset: number | null;
14
+ };
15
+ export type RequestRow = RequestEvent & { id: number; invocation: string; command: string };
16
+ export type TelemetryStore = {
17
+ record: (event: RequestEvent) => void;
18
+ read: (since: string) => readonly RequestRow[];
19
+ close: () => void;
20
+ };
21
+
22
+ export const Telemetry = Context.Reference<TelemetryStore | null>("gh/Telemetry", {
23
+ defaultValue: () => null,
24
+ });
25
+ export const CacheRequest = Context.Reference<boolean>("gh/CacheRequest", {
26
+ defaultValue: () => false,
27
+ });
28
+
29
+ export const templateEndpoint = (path: string): string => {
30
+ const pathname = path.replace(/^https?:\/\/[^/]+/, "").split(/[?#]/, 1)[0] ?? "";
31
+ return redactSensitiveText(
32
+ `/${pathname.replace(/^\//, "")}`
33
+ .replace(/\b[0-9a-f]{7,64}\b/gi, (part) => (/^\d+$/.test(part) ? ":id" : ":sha"))
34
+ .replace(/\/\d+(?=\/|$)/g, "/:id"),
35
+ );
36
+ };
37
+
38
+ export const recordRequest = (event: RequestEvent): Effect.Effect<void> =>
39
+ Effect.gen(function* () {
40
+ const store = yield* Telemetry;
41
+ try {
42
+ store?.record({
43
+ ...event,
44
+ endpoint:
45
+ event.outcome === "subprocess" ? event.endpoint : templateEndpoint(event.endpoint),
46
+ });
47
+ } catch {
48
+ /* Best effort. */
49
+ }
50
+ });
51
+
52
+ export const cacheEvent = (operation: string): RequestEvent => ({
53
+ ts: new Date().toISOString(),
54
+ method: "CACHE",
55
+ endpoint: `cache/${operation}`,
56
+ status: null,
57
+ outcome: "cache",
58
+ resource: null,
59
+ used: null,
60
+ remaining: null,
61
+ reset: null,
62
+ });
63
+
64
+ const API_VALUE_FLAGS = new Set([
65
+ "-X",
66
+ "--method",
67
+ "-H",
68
+ "--header",
69
+ "-f",
70
+ "-F",
71
+ "--field",
72
+ "--raw-field",
73
+ "--jq",
74
+ "-q",
75
+ "--template",
76
+ "-t",
77
+ "--input",
78
+ "--cache",
79
+ "-p",
80
+ "--preview",
81
+ "--hostname",
82
+ ]);
83
+
84
+ export const ghSubprocessEvent = (args: readonly string[], exitCode: number): RequestEvent => {
85
+ const api = args[0] === "api";
86
+ let endpoint: string | undefined;
87
+ let method: string | undefined;
88
+ let hasFields = false;
89
+ if (api) {
90
+ for (let index = 1; index < args.length; index++) {
91
+ const arg = args[index];
92
+ if (arg === undefined) continue;
93
+ if (arg === "--") {
94
+ endpoint ??= args[index + 1];
95
+ break;
96
+ }
97
+ if (!arg.startsWith("-")) {
98
+ endpoint ??= arg;
99
+ continue;
100
+ }
101
+ const equals = arg.indexOf("=");
102
+ const flag = equals < 0 ? arg : arg.slice(0, equals);
103
+ if (!API_VALUE_FLAGS.has(flag)) continue;
104
+ const value = equals < 0 ? args[++index] : arg.slice(equals + 1);
105
+ if (flag === "-X" || flag === "--method") method = value;
106
+ if (flag === "-f" || flag === "-F" || flag === "--field" || flag === "--raw-field")
107
+ hasFields = true;
108
+ }
109
+ }
110
+ return {
111
+ ...cacheEvent(""),
112
+ method: api ? (method ?? (hasFields ? "POST" : "GET")) : "GH",
113
+ endpoint: api ? templateEndpoint(endpoint ?? "") : args.slice(0, 2).join(" "),
114
+ status: exitCode >= 100 && exitCode <= 599 ? exitCode : null,
115
+ outcome: "subprocess",
116
+ };
117
+ };
118
+
119
+ const counts = (values: readonly string[]) => {
120
+ const groups = new Map<string, number>();
121
+ for (const value of values) groups.set(value, (groups.get(value) ?? 0) + 1);
122
+ return [...groups]
123
+ .map(([name, requests]) => ({ name, requests }))
124
+ .toSorted((a, b) => b.requests - a.requests || a.name.localeCompare(b.name));
125
+ };
126
+
127
+ const subprocessSpendsResource = (row: RequestRow, resource: string): boolean => {
128
+ if (row.outcome !== "subprocess") return false;
129
+ if (row.method === "GH") return true;
130
+ if (row.method === "GET" && row.endpoint === "/rate_limit") return false;
131
+ return row.endpoint === "/graphql" ? resource === "graphql" : resource !== "graphql";
132
+ };
133
+
134
+ export const summarizeRequests = (rows: readonly RequestRow[]) => {
135
+ const requests = rows.filter((row) => row.outcome !== "cache" && row.outcome !== "subprocess");
136
+ const subprocesses = rows.filter((row) => row.outcome === "subprocess");
137
+ const previous = new Map<string, { used: number; index: number }>();
138
+ let externalUsageEstimate = 0;
139
+ let intervals = 0;
140
+ for (const [index, row] of rows.entries()) {
141
+ if (row.outcome === "cache" || row.outcome === "subprocess") continue;
142
+ if (row.resource === null || row.used === null || row.reset === null) continue;
143
+ const resource = row.resource;
144
+ const key = `${resource}:${row.reset}`;
145
+ const before = previous.get(key);
146
+ if (before !== undefined && row.used >= before.used) {
147
+ const interval = rows.slice(before.index + 1, index + 1);
148
+ const own = interval.filter(
149
+ (candidate) =>
150
+ candidate.resource === resource &&
151
+ (candidate.reset === null || candidate.reset === row.reset) &&
152
+ candidate.status !== 304 &&
153
+ candidate.endpoint !== "/rate_limit",
154
+ ).length;
155
+ const subprocessCalls = interval.filter((candidate) =>
156
+ subprocessSpendsResource(candidate, resource),
157
+ ).length;
158
+ externalUsageEstimate += Math.max(0, row.used - before.used - own - subprocessCalls);
159
+ intervals++;
160
+ }
161
+ if (before === undefined || row.used >= before.used)
162
+ previous.set(key, { used: row.used, index });
163
+ }
164
+ const notModified = requests.filter((row) => row.status === 304).length;
165
+ return {
166
+ directHttpRequests: requests.length,
167
+ ghSubprocessCalls: subprocesses.length,
168
+ cacheHits: rows.filter((row) => row.outcome === "cache").length,
169
+ subprocessTopCommands: counts(subprocesses.map((row) => redactSensitiveText(row.command)))
170
+ .slice(0, 20)
171
+ .map(({ name, requests: calls }) => ({ name, calls })),
172
+ subprocessTopEndpoints: counts(subprocesses.map((row) => `${row.method} ${row.endpoint}`))
173
+ .slice(0, 20)
174
+ .map(({ name, requests: calls }) => ({ name, calls })),
175
+ directHttpByResource: counts(requests.map((row) => row.resource ?? "unknown")),
176
+ directHttpTopCommands: counts(requests.map((row) => redactSensitiveText(row.command))).slice(
177
+ 0,
178
+ 20,
179
+ ),
180
+ directHttpTopEndpoints: counts(requests.map((row) => `${row.method} ${row.endpoint}`)).slice(
181
+ 0,
182
+ 20,
183
+ ),
184
+ notModified,
185
+ ratio304: requests.length === 0 ? null : notModified / requests.length,
186
+ externalUsageEstimate: intervals === 0 ? null : externalUsageEstimate,
187
+ observedIntervals: intervals,
188
+ caveat:
189
+ "Estimate within observed reset windows only. Missing headers, concurrent requests, different tokens and dropped telemetry reduce accuracy. Direct HTTP counts are exact. Each gh subprocess call may make zero or more HTTP requests; its HTTP status and quota headers are unavailable. For this estimate, subprocess calls count as one request each. Cache hits count ReadCache only.",
190
+ };
191
+ };
@@ -1,10 +1,16 @@
1
+ import { ghSubprocessEvent, recordRequest } from "./telemetry";
1
2
  import { ChildProcess, ChildProcessSpawner } from "effect/process";
2
- import { Context, Effect, Layer, Stream } from "effect";
3
+ import { Context, Effect, Layer, Schema, Stream } from "effect";
3
4
 
4
5
  import type { GitHubRepoConfig } from "#config";
5
6
  import type { RepoInfo } from "#gh/types";
6
7
  import type { RepoRef } from "./keys";
7
8
 
9
+ import {
10
+ bunMarkdownOutline,
11
+ findCheckboxLines,
12
+ validateCheckboxEdit,
13
+ } from "#gh/pr/checkbox-markdown";
8
14
  import { GH_BINARY } from "#gh/config";
9
15
  import { ghEnvironment } from "#gh/environment";
10
16
  import { GitHubAuthError, GitHubCommandError, GitHubNotFoundError } from "#gh/errors";
@@ -79,11 +85,20 @@ export type GhResult = {
79
85
  };
80
86
 
81
87
  export type GhError = GitHubCommandError | GitHubAuthError | GitHubNotFoundError;
88
+ export type PRCheckboxWrite = {
89
+ readonly repo: RepoRef;
90
+ readonly pr: number;
91
+ readonly expectedBody: string;
92
+ readonly boxOffset: number;
93
+ readonly replacement: " " | "x";
94
+ readonly selectionText: readonly string[];
95
+ };
82
96
 
83
97
  export class GitHubService extends Context.Service<
84
98
  GitHubService,
85
99
  {
86
100
  readonly runGh: (args: string[]) => Effect.Effect<GhResult, GhError>;
101
+ readonly writePRCheckbox?: (opts: PRCheckboxWrite) => Effect.Effect<GhResult, GhError>;
87
102
  readonly runGhJson: <T>(args: string[]) => Effect.Effect<T, GhError>;
88
103
  readonly runGraphQL: (
89
104
  query: string,
@@ -182,13 +197,17 @@ export class GitHubService extends Context.Service<
182
197
 
183
198
  const proc = yield* executor.spawn(command);
184
199
 
185
- const stdoutChunk = yield* proc.stdout.pipe(Stream.decodeText(), Stream.runCollect);
200
+ const [stdoutChunk, stderrChunk] = yield* Effect.all(
201
+ [
202
+ proc.stdout.pipe(Stream.decodeText(), Stream.runCollect),
203
+ proc.stderr.pipe(Stream.decodeText(), Stream.runCollect),
204
+ ],
205
+ { concurrency: "unbounded" },
206
+ );
186
207
  const stdout = stdoutChunk.join("");
187
-
188
- const stderrChunk = yield* proc.stderr.pipe(Stream.decodeText(), Stream.runCollect);
189
208
  const stderr = stderrChunk.join("");
190
-
191
209
  const exitCode = yield* proc.exitCode;
210
+ yield* recordRequest(ghSubprocessEvent(args, Number(exitCode)));
192
211
 
193
212
  return {
194
213
  stdout,
@@ -246,7 +265,7 @@ export class GitHubService extends Context.Service<
246
265
  const quotaFilter =
247
266
  resource === undefined
248
267
  ? '.resources | to_entries[] | "\\(.key): reset time: \\(.value.reset | todateiso8601); remaining quota: \\(.value.remaining)"'
249
- : `.resources.${resource} | "reset time: \\(.reset | todateiso8601); remaining quota: \\(.remaining)"`;
268
+ : `.resources.${resource} | "resource: ${resource}; reset time: \\(.reset | todateiso8601); remaining quota: \\(.remaining)"`;
250
269
  const quota = yield* executeGh(["api", "rate_limit", "--jq", quotaFilter]).pipe(
251
270
  Effect.orElseSucceed(() => null),
252
271
  );
@@ -320,6 +339,75 @@ export class GitHubService extends Context.Service<
320
339
  });
321
340
  };
322
341
 
342
+ const writePRCheckbox = Effect.fn("GitHubService.writePRCheckbox")(function* (
343
+ opts: PRCheckboxWrite,
344
+ ) {
345
+ const path = `repos/${opts.repo.owner}/${opts.repo.name}/pulls/${opts.pr}`;
346
+ const response = yield* githubApi<unknown>({ path });
347
+ const parsed = yield* Effect.try({
348
+ try: () =>
349
+ Schema.decodeUnknownSync(Schema.Struct({ body: Schema.NullOr(Schema.String) }))(
350
+ response.body,
351
+ ),
352
+ catch: (error) =>
353
+ new GitHubCommandError({
354
+ command: `GET ${path}`,
355
+ exitCode: 0,
356
+ message: `GitHub returned an invalid PR body: ${String(error)}`,
357
+ stderr: `GitHub returned an invalid PR body: ${String(error)}`,
358
+ }),
359
+ });
360
+ const body = parsed.body ?? "";
361
+ if (body !== opts.expectedBody) {
362
+ return yield* new GitHubCommandError({
363
+ command: "gh-tool pr checkbox",
364
+ exitCode: 1,
365
+ stderr: `PR #${opts.pr} body changed before the checkbox update`,
366
+ message: `Refusing to update PR #${opts.pr}: its body changed after the first read`,
367
+ hint: "Re-read the pull request and retry with the current task text.",
368
+ });
369
+ }
370
+
371
+ const checkboxes = findCheckboxLines(
372
+ body,
373
+ opts.selectionText[0] ?? "",
374
+ null,
375
+ bunMarkdownOutline,
376
+ );
377
+ if (
378
+ !checkboxes.some((checkbox) => checkbox.offset === opts.boxOffset) ||
379
+ body[opts.boxOffset] === opts.replacement
380
+ ) {
381
+ return yield* new GitHubCommandError({
382
+ command: "gh-tool pr checkbox",
383
+ exitCode: 1,
384
+ stderr: "Refusing an invalid PR checkbox position",
385
+ message: "Refusing to update a character that is not a task checkbox",
386
+ });
387
+ }
388
+
389
+ yield* Effect.try({
390
+ try: () => validateCheckboxEdit(body, opts.boxOffset, opts.replacement),
391
+ catch: () =>
392
+ new GitHubCommandError({
393
+ command: "gh-tool pr checkbox",
394
+ exitCode: 1,
395
+ stderr: "PR body structure is ambiguous; tick this item in the GitHub UI",
396
+ message: "Refusing to update the PR checkbox because its source is ambiguous",
397
+ hint: "PR body structure is ambiguous; tick this item in the GitHub UI.",
398
+ }),
399
+ });
400
+
401
+ const updatedBody =
402
+ body.slice(0, opts.boxOffset) + opts.replacement + body.slice(opts.boxOffset + 1);
403
+ const args = ["api", "--method", "PATCH", path, "-f", `body=${updatedBody}`];
404
+ const outboundText = [...args];
405
+ outboundText[5] = "body=[GitHub PR body]";
406
+ outboundText.push(...opts.selectionText);
407
+ yield* validateOutboundText(outboundText.join("\n"), "gh-tool pr checkbox");
408
+ return yield* runGhAttempt(args);
409
+ });
410
+
323
411
  const runGhJson = <T>(args: string[]) =>
324
412
  Effect.gen(function* () {
325
413
  const result = yield* runGh(args);
@@ -410,6 +498,7 @@ export class GitHubService extends Context.Service<
410
498
 
411
499
  return {
412
500
  runGh,
501
+ writePRCheckbox,
413
502
  runGhJson,
414
503
  runGraphQL,
415
504
  apiRequest: githubApi,
@@ -1,8 +1,9 @@
1
1
  import { Effect } from "effect";
2
+ import { GitHubCommandError } from "#gh/errors";
2
3
  import { GitHubService } from "#gh/read/transport";
3
4
  import { invalidate } from "./cache";
4
5
  import type { Tag } from "./keys";
5
- import type { GhError } from "#gh/read/transport";
6
+ import type { GhError, PRCheckboxWrite } from "#gh/read/transport";
6
7
  import type { GitHubApiRequest } from "#gh/read/api";
7
8
 
8
9
  const mutate = <A, R>(effect: Effect.Effect<A, GhError, R>, touched: readonly Tag[]) =>
@@ -15,6 +16,22 @@ const run = Effect.fn("writes.run")(function* (argv: string[], touched: readonly
15
16
  const gh = yield* GitHubService;
16
17
  return yield* mutate(gh.runGh(argv), touched);
17
18
  });
19
+ const prCheckbox = Effect.fn("writes.prCheckbox")(function* (
20
+ opts: PRCheckboxWrite,
21
+ touched: readonly Tag[],
22
+ ) {
23
+ const gh = yield* GitHubService;
24
+ if (gh.writePRCheckbox === undefined) {
25
+ return yield* new GitHubCommandError({
26
+ command: "gh-tool pr checkbox",
27
+ exitCode: 1,
28
+ stderr: "PR checkbox writes are unavailable",
29
+ message:
30
+ "Refusing to update the PR checkbox because the GitHub service cannot verify the body",
31
+ });
32
+ }
33
+ return yield* mutate(gh.writePRCheckbox(opts), touched);
34
+ });
18
35
  const json = <A>(argv: string[], touched: readonly Tag[]) =>
19
36
  Effect.gen(function* () {
20
37
  const gh = yield* GitHubService;
@@ -34,4 +51,4 @@ const api = <A>(request: GitHubApiRequest, touched: readonly Tag[]) =>
34
51
  const gh = yield* GitHubService;
35
52
  return yield* mutate(gh.apiRequest<A>(request), touched);
36
53
  });
37
- export const writes = { run, json, graphql, api };
54
+ export const writes = { run, prCheckbox, json, graphql, api };
@@ -298,7 +298,7 @@ const fetchAnnotations = Effect.fn("workflow.fetchAnnotations")(function* (opts:
298
298
  const parts = opts.repo.split("/");
299
299
  if (parts.length !== 2 || !parts[0] || !parts[1]) {
300
300
  return yield* new GitHubCommandError({
301
- message: `Invalid --repo format: "${opts.repo}". Expected "owner/name" (e.g. "blogic-cz/agent-tools").`,
301
+ message: `Invalid --repo format: "${opts.repo}". Expected "owner/name" (e.g. "example-org/example-repo").`,
302
302
  command: "workflow annotations",
303
303
  exitCode: 1,
304
304
  stderr: "",
@@ -574,7 +574,7 @@ export const fetchJobLogs = Effect.fn("workflow.fetchJobLogs")(function* (
574
574
  const parts = opts.repo.split("/");
575
575
  if (parts.length !== 2 || !parts[0] || !parts[1]) {
576
576
  return yield* new GitHubCommandError({
577
- message: `Invalid --repo format: "${opts.repo}". Expected "owner/name" (e.g. "blogic-cz/agent-tools").`,
577
+ message: `Invalid --repo format: "${opts.repo}". Expected "owner/name" (e.g. "example-org/example-repo").`,
578
578
  command: "workflow job-logs",
579
579
  exitCode: 1,
580
580
  stderr: "",