@blogic-cz/agent-tools 1.5.2 → 1.5.4
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 +4 -4
- package/dist/audit-tool/main.d.ts +3 -0
- package/dist/audit-tool/main.d.ts.map +1 -0
- package/dist/az-tool/main.d.ts +3 -0
- package/dist/az-tool/main.d.ts.map +1 -0
- package/dist/azdo-tool/main.d.ts +3 -0
- package/dist/azdo-tool/main.d.ts.map +1 -0
- package/dist/credential-guard/index.d.ts.map +1 -1
- package/dist/db-tool/main.d.ts +3 -0
- package/dist/db-tool/main.d.ts.map +1 -0
- package/dist/gh-tool/api.d.ts.map +1 -1
- package/dist/gh-tool/gist.d.ts.map +1 -1
- package/dist/gh-tool/main.d.ts +3 -0
- package/dist/gh-tool/main.d.ts.map +1 -0
- package/dist/gh-tool/pr/core.d.ts.map +1 -1
- package/dist/gh-tool/service.d.ts.map +1 -1
- package/dist/gh-tool/text-input.d.ts +4 -0
- package/dist/gh-tool/text-input.d.ts.map +1 -1
- package/dist/gh-tool/workflow.d.ts.map +1 -1
- package/dist/k8s-tool/main.d.ts +3 -0
- package/dist/k8s-tool/main.d.ts.map +1 -0
- package/dist/logs-tool/index.d.ts +1 -19
- package/dist/logs-tool/index.d.ts.map +1 -1
- package/dist/logs-tool/main.d.ts +21 -0
- package/dist/logs-tool/main.d.ts.map +1 -0
- package/dist/observability-tool/main.d.ts +3 -0
- package/dist/observability-tool/main.d.ts.map +1 -0
- package/dist/session-tool/index.d.ts +1 -11
- package/dist/session-tool/index.d.ts.map +1 -1
- package/dist/session-tool/main.d.ts +13 -0
- package/dist/session-tool/main.d.ts.map +1 -0
- package/dist/shared/effect-version.d.ts +4 -0
- package/dist/shared/effect-version.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/audit-tool/index.ts +3 -109
- package/src/audit-tool/main.ts +111 -0
- package/src/az-tool/index.ts +3 -151
- package/src/az-tool/main.ts +153 -0
- package/src/azdo-tool/index.ts +3 -220
- package/src/azdo-tool/main.ts +222 -0
- package/src/credential-guard/index.ts +102 -4
- package/src/db-tool/index.ts +3 -260
- package/src/db-tool/main.ts +262 -0
- package/src/gh-tool/api.ts +21 -1
- package/src/gh-tool/gist.ts +7 -1
- package/src/gh-tool/index.ts +3 -256
- package/src/gh-tool/main.ts +258 -0
- package/src/gh-tool/pr/core.ts +52 -2
- package/src/gh-tool/service.ts +9 -5
- package/src/gh-tool/text-input.ts +63 -4
- package/src/gh-tool/workflow.ts +1 -0
- package/src/k8s-tool/index.ts +3 -487
- package/src/k8s-tool/main.ts +489 -0
- package/src/logs-tool/index.ts +3 -247
- package/src/logs-tool/main.ts +249 -0
- package/src/observability-tool/index.ts +3 -34
- package/src/observability-tool/main.ts +36 -0
- package/src/session-tool/index.ts +3 -337
- package/src/session-tool/main.ts +339 -0
- package/src/shared/effect-version.ts +32 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { Command, Flag } from "effect/unstable/cli";
|
|
3
|
+
import { BunRuntime, BunServices } from "@effect/platform-bun";
|
|
4
|
+
import { Effect, Layer, Option } from "effect";
|
|
5
|
+
|
|
6
|
+
import type { SchemaMode } from "./types";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
makeSchemaCommand,
|
|
10
|
+
formatOption,
|
|
11
|
+
formatOutput,
|
|
12
|
+
logText,
|
|
13
|
+
renderCauseToStderr,
|
|
14
|
+
VERSION,
|
|
15
|
+
} from "#shared";
|
|
16
|
+
import { AuditServiceLayer, withAudit } from "#shared/audit";
|
|
17
|
+
import { ConfigService, ConfigServiceLayer, getDefaultEnvironment } from "#config";
|
|
18
|
+
import { DbConfigService, makeDbConfigLayer } from "./config-service";
|
|
19
|
+
import { DbConnectionError } from "./errors";
|
|
20
|
+
import { DbService } from "./service";
|
|
21
|
+
import { DbSqlClient } from "./sql-client";
|
|
22
|
+
|
|
23
|
+
// Extract --profile from argv before @effect/cli parsing
|
|
24
|
+
// so we can build the correct config layer.
|
|
25
|
+
const profileIndex = process.argv.indexOf("--profile");
|
|
26
|
+
const profileArg = profileIndex !== -1 ? process.argv[profileIndex + 1] : undefined;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Resolve environment from explicit --env flag, config defaultEnvironment, or fail with hint.
|
|
30
|
+
*/
|
|
31
|
+
const resolveEnv = (envOption: Option.Option<string>) =>
|
|
32
|
+
Effect.gen(function* () {
|
|
33
|
+
const explicit = Option.getOrUndefined(envOption);
|
|
34
|
+
if (explicit) return explicit;
|
|
35
|
+
|
|
36
|
+
const config = yield* ConfigService;
|
|
37
|
+
const defaultEnv = getDefaultEnvironment(config);
|
|
38
|
+
|
|
39
|
+
if (defaultEnv === "prod") {
|
|
40
|
+
return yield* new DbConnectionError({
|
|
41
|
+
message:
|
|
42
|
+
"Implicit prod access blocked. Config defaultEnvironment is 'prod' but --env was not passed explicitly.",
|
|
43
|
+
environment: "(prod-safety)",
|
|
44
|
+
hint: "Pass --env prod explicitly to confirm production access, or change defaultEnvironment to a non-prod value.",
|
|
45
|
+
nextCommand: 'agent-tools-db sql --env prod --sql "SELECT 1"',
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (defaultEnv) return defaultEnv;
|
|
50
|
+
|
|
51
|
+
return yield* new DbConnectionError({
|
|
52
|
+
message:
|
|
53
|
+
"No environment specified. Use --env <name> or set defaultEnvironment in agent-tools.json5.",
|
|
54
|
+
environment: "(not specified)",
|
|
55
|
+
hint: 'Set defaultEnvironment in agent-tools.json5 (e.g. defaultEnvironment: "local") or pass --env explicitly.',
|
|
56
|
+
nextCommand: 'agent-tools-db sql --env local --sql "SELECT 1"',
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const sqlCommand = Command.make(
|
|
61
|
+
"sql",
|
|
62
|
+
{
|
|
63
|
+
env: Flag.optional(Flag.String("env")).pipe(
|
|
64
|
+
Flag.withDescription(
|
|
65
|
+
"Target database environment name (e.g. local, test, prod). Falls back to defaultEnvironment in config.",
|
|
66
|
+
),
|
|
67
|
+
),
|
|
68
|
+
sql: Flag.String("sql").pipe(Flag.withDescription("SQL query to execute")),
|
|
69
|
+
limit: Flag.optional(Flag.Int("limit")).pipe(
|
|
70
|
+
Flag.withDescription(
|
|
71
|
+
"Max rows to return (default 50). Use 0 for no cap. Prefer a SQL LIMIT for large tables.",
|
|
72
|
+
),
|
|
73
|
+
),
|
|
74
|
+
format: formatOption,
|
|
75
|
+
profile: Flag.optional(Flag.String("profile")).pipe(
|
|
76
|
+
Flag.withDescription("Database profile name from agent-tools.json5 (if multiple configured)"),
|
|
77
|
+
),
|
|
78
|
+
},
|
|
79
|
+
({ env, sql, limit, format }) =>
|
|
80
|
+
Effect.gen(function* () {
|
|
81
|
+
const resolvedEnv = yield* resolveEnv(env);
|
|
82
|
+
const db = yield* DbService;
|
|
83
|
+
const result = yield* db.executeQuery(resolvedEnv, sql, Option.getOrUndefined(limit));
|
|
84
|
+
yield* logText(formatOutput(result, format));
|
|
85
|
+
}),
|
|
86
|
+
).pipe(Command.withDescription("Execute a SQL query"));
|
|
87
|
+
|
|
88
|
+
const schemaCommand = Command.make(
|
|
89
|
+
"schema",
|
|
90
|
+
{
|
|
91
|
+
env: Flag.optional(Flag.String("env")).pipe(
|
|
92
|
+
Flag.withDescription(
|
|
93
|
+
"Target database environment name (e.g. local, test, prod). Falls back to defaultEnvironment in config.",
|
|
94
|
+
),
|
|
95
|
+
),
|
|
96
|
+
mode: Flag.Literals("mode", ["tables", "columns", "full", "relationships"]).pipe(
|
|
97
|
+
Flag.withDescription(
|
|
98
|
+
"Schema introspection mode: tables (list all), columns (show columns for --table), full (all tables with columns), relationships (foreign keys)",
|
|
99
|
+
),
|
|
100
|
+
),
|
|
101
|
+
table: Flag.String("table").pipe(
|
|
102
|
+
Flag.withDescription("Table name (required for --mode columns)"),
|
|
103
|
+
Flag.optional,
|
|
104
|
+
),
|
|
105
|
+
format: formatOption,
|
|
106
|
+
profile: Flag.optional(Flag.String("profile")).pipe(
|
|
107
|
+
Flag.withDescription("Database profile name from agent-tools.json5 (if multiple configured)"),
|
|
108
|
+
),
|
|
109
|
+
},
|
|
110
|
+
({ env, mode, table, format }) =>
|
|
111
|
+
Effect.gen(function* () {
|
|
112
|
+
const resolvedEnv = yield* resolveEnv(env);
|
|
113
|
+
const db = yield* DbService;
|
|
114
|
+
const result = yield* db.executeSchemaQuery(
|
|
115
|
+
resolvedEnv,
|
|
116
|
+
mode as SchemaMode,
|
|
117
|
+
Option.getOrUndefined(table),
|
|
118
|
+
);
|
|
119
|
+
yield* logText(formatOutput(result, format));
|
|
120
|
+
}),
|
|
121
|
+
).pipe(Command.withDescription("Introspect database schema (tables, columns, relationships)"));
|
|
122
|
+
|
|
123
|
+
const envsCommand = Command.make(
|
|
124
|
+
"envs",
|
|
125
|
+
{
|
|
126
|
+
format: formatOption,
|
|
127
|
+
profile: Flag.optional(Flag.String("profile")).pipe(
|
|
128
|
+
Flag.withDescription("Database profile name from agent-tools.json5 (if multiple configured)"),
|
|
129
|
+
),
|
|
130
|
+
},
|
|
131
|
+
({ format }) =>
|
|
132
|
+
Effect.gen(function* () {
|
|
133
|
+
const config = yield* ConfigService;
|
|
134
|
+
const dbConfig = yield* DbConfigService;
|
|
135
|
+
const environments = dbConfig ? Object.keys(dbConfig.environments) : [];
|
|
136
|
+
const result = {
|
|
137
|
+
success: true as const,
|
|
138
|
+
environments,
|
|
139
|
+
default: getDefaultEnvironment(config) ?? null,
|
|
140
|
+
message: `${environments.length} environment(s) configured`,
|
|
141
|
+
executionTimeMs: 0,
|
|
142
|
+
};
|
|
143
|
+
yield* logText(formatOutput(result, format));
|
|
144
|
+
}),
|
|
145
|
+
).pipe(
|
|
146
|
+
Command.withDescription("List configured database environments and the default (no network)"),
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
const ACTIVITY_SQL = `SELECT pid, usename AS "user", state,
|
|
150
|
+
date_trunc('second', now() - xact_start) AS xact_age,
|
|
151
|
+
date_trunc('second', now() - query_start) AS query_age,
|
|
152
|
+
wait_event_type, wait_event, left(query, 200) AS query
|
|
153
|
+
FROM pg_stat_activity
|
|
154
|
+
WHERE pid <> pg_backend_pid() AND state IS NOT NULL
|
|
155
|
+
ORDER BY xact_start ASC NULLS LAST`;
|
|
156
|
+
|
|
157
|
+
const LOCKS_SQL = `SELECT blocking.pid AS blocking_pid, blocking.usename AS blocking_user,
|
|
158
|
+
left(blocking.query, 150) AS blocking_query,
|
|
159
|
+
blocked.pid AS blocked_pid, blocked.usename AS blocked_user,
|
|
160
|
+
left(blocked.query, 150) AS blocked_query
|
|
161
|
+
FROM pg_stat_activity blocked
|
|
162
|
+
JOIN pg_stat_activity blocking ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
|
|
163
|
+
ORDER BY blocking.pid, blocked.pid`;
|
|
164
|
+
|
|
165
|
+
const GRANTS_SQL = `SELECT grantee, table_schema AS schema, 'table' AS kind, table_name AS object,
|
|
166
|
+
string_agg(privilege_type, ', ' ORDER BY privilege_type) AS privileges
|
|
167
|
+
FROM information_schema.role_table_grants
|
|
168
|
+
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
169
|
+
GROUP BY grantee, table_schema, table_name
|
|
170
|
+
UNION ALL
|
|
171
|
+
SELECT grantee, object_schema AS schema, 'sequence' AS kind, object_name AS object,
|
|
172
|
+
string_agg(privilege_type, ', ' ORDER BY privilege_type) AS privileges
|
|
173
|
+
FROM information_schema.role_usage_grants
|
|
174
|
+
WHERE object_type = 'SEQUENCE' AND object_schema NOT IN ('pg_catalog', 'information_schema')
|
|
175
|
+
GROUP BY grantee, object_schema, object_name
|
|
176
|
+
ORDER BY grantee, schema, kind, object`;
|
|
177
|
+
|
|
178
|
+
const makeDiagnosticCommand = (name: string, description: string, sql: string) =>
|
|
179
|
+
Command.make(
|
|
180
|
+
name,
|
|
181
|
+
{
|
|
182
|
+
env: Flag.optional(Flag.String("env")).pipe(
|
|
183
|
+
Flag.withDescription(
|
|
184
|
+
"Target database environment name (e.g. local, test, prod). Falls back to defaultEnvironment in config.",
|
|
185
|
+
),
|
|
186
|
+
),
|
|
187
|
+
limit: Flag.optional(Flag.Int("limit")).pipe(
|
|
188
|
+
Flag.withDescription("Max rows to return (default 50). Use 0 for no cap."),
|
|
189
|
+
),
|
|
190
|
+
format: formatOption,
|
|
191
|
+
profile: Flag.optional(Flag.String("profile")).pipe(
|
|
192
|
+
Flag.withDescription(
|
|
193
|
+
"Database profile name from agent-tools.json5 (if multiple configured)",
|
|
194
|
+
),
|
|
195
|
+
),
|
|
196
|
+
},
|
|
197
|
+
({ env, limit, format }) =>
|
|
198
|
+
Effect.gen(function* () {
|
|
199
|
+
const resolvedEnv = yield* resolveEnv(env);
|
|
200
|
+
const db = yield* DbService;
|
|
201
|
+
const result = yield* db.executeQuery(resolvedEnv, sql, Option.getOrUndefined(limit));
|
|
202
|
+
yield* logText(formatOutput(result, format));
|
|
203
|
+
}),
|
|
204
|
+
).pipe(Command.withDescription(description));
|
|
205
|
+
|
|
206
|
+
const activityCommand = makeDiagnosticCommand(
|
|
207
|
+
"activity",
|
|
208
|
+
"Show live sessions from pg_stat_activity (state, transaction/query age, wait event), longest-running first",
|
|
209
|
+
ACTIVITY_SQL,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const locksCommand = makeDiagnosticCommand(
|
|
213
|
+
"locks",
|
|
214
|
+
"Show blocking chains (blocker -> blocked) from pg_blocking_pids + pg_stat_activity",
|
|
215
|
+
LOCKS_SQL,
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
const grantsCommand = makeDiagnosticCommand(
|
|
219
|
+
"grants",
|
|
220
|
+
"Show per-role table/sequence privileges by schema (excludes system schemas)",
|
|
221
|
+
GRANTS_SQL,
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
const commandsCommand = makeSchemaCommand(() => mainCommand);
|
|
225
|
+
|
|
226
|
+
const mainCommand = Command.make("db-tool", {}).pipe(
|
|
227
|
+
Command.withDescription("Database Query Tool for Coding Agents"),
|
|
228
|
+
Command.withSubcommands([
|
|
229
|
+
sqlCommand,
|
|
230
|
+
schemaCommand,
|
|
231
|
+
activityCommand,
|
|
232
|
+
locksCommand,
|
|
233
|
+
grantsCommand,
|
|
234
|
+
envsCommand,
|
|
235
|
+
commandsCommand,
|
|
236
|
+
]),
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
const cli = Command.run(mainCommand, {
|
|
240
|
+
version: VERSION,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
const dbConfigLayer = makeDbConfigLayer(profileArg);
|
|
244
|
+
|
|
245
|
+
const MainLayer = DbService.layer.pipe(
|
|
246
|
+
// provideMerge (not provide) so DbConfigService stays in the program context for the `envs` command,
|
|
247
|
+
// which reads it directly rather than going through DbService.
|
|
248
|
+
Layer.provideMerge(DbSqlClient.layer),
|
|
249
|
+
Layer.provideMerge(dbConfigLayer),
|
|
250
|
+
Layer.provideMerge(ConfigServiceLayer),
|
|
251
|
+
Layer.provideMerge(BunServices.layer),
|
|
252
|
+
Layer.provideMerge(AuditServiceLayer),
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
const program = withAudit("db", cli).pipe(
|
|
256
|
+
Effect.provide(MainLayer),
|
|
257
|
+
Effect.tapCause(renderCauseToStderr),
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
BunRuntime.runMain(program, {
|
|
261
|
+
disableErrorReporting: true,
|
|
262
|
+
});
|
package/src/gh-tool/api.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { retryTransient } from "#shared/retry-transient";
|
|
|
4
4
|
|
|
5
5
|
import { GitHubAuthError, GitHubCommandError, GitHubNotFoundError } from "./errors";
|
|
6
6
|
import type { GitHubApiError } from "./errors";
|
|
7
|
+
import { validateOutboundText } from "./text-input";
|
|
7
8
|
|
|
8
9
|
// Direct HTTP, not `gh api`: the CLI collapses every failure into a non-zero exit and
|
|
9
10
|
// loses the status code, but merge-async answers 202 (accepted), 200 (already merged or
|
|
@@ -66,9 +67,28 @@ export type GitHubApiRequest = {
|
|
|
66
67
|
|
|
67
68
|
const MAX_API_RETRIES = 2;
|
|
68
69
|
|
|
70
|
+
const outboundBodyText = (value: unknown): string => {
|
|
71
|
+
if (typeof value === "string") return value;
|
|
72
|
+
if (Array.isArray(value)) return value.map(outboundBodyText).join("\n");
|
|
73
|
+
if (value !== null && typeof value === "object")
|
|
74
|
+
return Object.values(value).map(outboundBodyText).join("\n");
|
|
75
|
+
return "";
|
|
76
|
+
};
|
|
77
|
+
|
|
69
78
|
const githubApiAttempt = Effect.fn("gh.githubApiAttempt")(function* <T>(opts: GitHubApiRequest) {
|
|
70
|
-
const token = yield* resolveGitHubToken();
|
|
71
79
|
const method = opts.method ?? "GET";
|
|
80
|
+
const bodyText = yield* Effect.try({
|
|
81
|
+
try: () => outboundBodyText(opts.body),
|
|
82
|
+
catch: () =>
|
|
83
|
+
new GitHubCommandError({
|
|
84
|
+
command: `gh-tool ${method} ${opts.path}`,
|
|
85
|
+
exitCode: 1,
|
|
86
|
+
stderr: "Refusing to publish an invalid request body",
|
|
87
|
+
message: "Refusing to publish an invalid request body",
|
|
88
|
+
}),
|
|
89
|
+
});
|
|
90
|
+
yield* validateOutboundText(`${opts.path}\n${bodyText}`, `gh-tool ${method} ${opts.path}`);
|
|
91
|
+
const token = yield* resolveGitHubToken();
|
|
72
92
|
const url = `${GITHUB_API_ROOT}/${opts.path.replace(/^\//, "")}`;
|
|
73
93
|
|
|
74
94
|
const response = yield* Effect.tryPromise({
|
package/src/gh-tool/gist.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Effect, Option } from "effect";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
|
|
5
5
|
import { formatOption, logFormatted } from "#shared";
|
|
6
|
-
import { isSensitivePath, resolveOptionalTextInput } from "#gh/text-input";
|
|
6
|
+
import { isSensitivePath, resolveOptionalTextInput, validateOutboundFile } from "#gh/text-input";
|
|
7
7
|
import { GitHubCommandError } from "./errors";
|
|
8
8
|
import { GitHubService } from "./service";
|
|
9
9
|
|
|
@@ -294,6 +294,8 @@ export const createGist = Effect.fn("gist.createGist")(function* (opts: {
|
|
|
294
294
|
}) {
|
|
295
295
|
const gh = yield* GitHubService;
|
|
296
296
|
|
|
297
|
+
for (const path of opts.paths) yield* validateOutboundFile(path, "gh-tool gist create");
|
|
298
|
+
|
|
297
299
|
const args = ["gist", "create", ...opts.paths];
|
|
298
300
|
|
|
299
301
|
if (opts.description !== null) {
|
|
@@ -339,6 +341,10 @@ const editGist = Effect.fn("gist.editGist")(function* (opts: {
|
|
|
339
341
|
}) {
|
|
340
342
|
const gh = yield* GitHubService;
|
|
341
343
|
|
|
344
|
+
for (const path of [opts.sourcePath, opts.add]) {
|
|
345
|
+
if (path !== null) yield* validateOutboundFile(path, "gh-tool gist edit");
|
|
346
|
+
}
|
|
347
|
+
|
|
342
348
|
const args = ["gist", "edit", opts.id];
|
|
343
349
|
const changes: string[] = [];
|
|
344
350
|
|
package/src/gh-tool/index.ts
CHANGED
|
@@ -1,258 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import {
|
|
3
|
-
import { BunRuntime, BunServices } from "@effect/platform-bun";
|
|
4
|
-
import { Effect, Layer } from "effect";
|
|
2
|
+
import { checkEffectVersion } from "#shared/effect-version";
|
|
5
3
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { ConfigServiceLayer } from "#config";
|
|
9
|
-
import {
|
|
10
|
-
issueCloseCommand,
|
|
11
|
-
issueCommentCommand,
|
|
12
|
-
issueCommentsCommand,
|
|
13
|
-
issueCreateCommand,
|
|
14
|
-
issueEditCommand,
|
|
15
|
-
issueListCommand,
|
|
16
|
-
issueReopenCommand,
|
|
17
|
-
issueSnapshotBatchCommand,
|
|
18
|
-
issueTriageCommand,
|
|
19
|
-
issueViewCommand,
|
|
20
|
-
} from "./issue";
|
|
21
|
-
import {
|
|
22
|
-
prViewCommand,
|
|
23
|
-
prStatusCommand,
|
|
24
|
-
prListCommand,
|
|
25
|
-
prCreateCommand,
|
|
26
|
-
prCloseCommand,
|
|
27
|
-
prEditCommand,
|
|
28
|
-
prMergeCommand,
|
|
29
|
-
prReadyCommand,
|
|
30
|
-
prStackCommand,
|
|
31
|
-
prRequestReviewCommand,
|
|
32
|
-
prThreadsCommand,
|
|
33
|
-
prCommentsCommand,
|
|
34
|
-
prIssueCommentsCommand,
|
|
35
|
-
prIssueCommentsLatestCommand,
|
|
36
|
-
prCommentCommand,
|
|
37
|
-
prDiscussionSummaryCommand,
|
|
38
|
-
prFeedbackCommand,
|
|
39
|
-
prReplyCommand,
|
|
40
|
-
prResolveCommand,
|
|
41
|
-
prReviewCommand,
|
|
42
|
-
prReviewsCommand,
|
|
43
|
-
prLastHumanReviewerCommand,
|
|
44
|
-
prSubmitReviewCommand,
|
|
45
|
-
prChecksCommand,
|
|
46
|
-
prChecksFailedCommand,
|
|
47
|
-
prRerunChecksCommand,
|
|
48
|
-
prTriggerChecksCommand,
|
|
49
|
-
prReplyAndResolveCommand,
|
|
50
|
-
prReviewTriageBatchCommand,
|
|
51
|
-
prReviewTriageCommand,
|
|
52
|
-
prWaitMergeableCommand,
|
|
53
|
-
prWatchCommand,
|
|
54
|
-
} from "./pr/index";
|
|
55
|
-
import { branchRenameCommand } from "./branch";
|
|
56
|
-
import {
|
|
57
|
-
gistCreateCommand,
|
|
58
|
-
gistDeleteCommand,
|
|
59
|
-
gistEditCommand,
|
|
60
|
-
gistListCommand,
|
|
61
|
-
gistViewCommand,
|
|
62
|
-
} from "./gist";
|
|
63
|
-
import {
|
|
64
|
-
releaseCreateCommand,
|
|
65
|
-
releaseDeleteCommand,
|
|
66
|
-
releaseEditCommand,
|
|
67
|
-
releaseListCommand,
|
|
68
|
-
releaseStatusCommand,
|
|
69
|
-
releaseViewCommand,
|
|
70
|
-
} from "./release";
|
|
71
|
-
import { repoInfoCommand, repoListCommand, repoSearchCodeCommand } from "./repo";
|
|
72
|
-
import { GitHubService } from "./service";
|
|
73
|
-
import {
|
|
74
|
-
workflowAnnotationsCommand,
|
|
75
|
-
workflowCancelCommand,
|
|
76
|
-
workflowJobLogsCommand,
|
|
77
|
-
workflowJobsCommand,
|
|
78
|
-
workflowListCommand,
|
|
79
|
-
workflowLogsCommand,
|
|
80
|
-
workflowRerunCommand,
|
|
81
|
-
workflowRunCommand,
|
|
82
|
-
workflowViewCommand,
|
|
83
|
-
workflowWatchCommand,
|
|
84
|
-
} from "./workflow";
|
|
85
|
-
|
|
86
|
-
const prCommand = Command.make("pr", {}).pipe(
|
|
87
|
-
Command.withDescription("Pull request operations (view, create, merge, reviews, checks)"),
|
|
88
|
-
Command.withSubcommands([
|
|
89
|
-
prViewCommand,
|
|
90
|
-
prStatusCommand,
|
|
91
|
-
prListCommand,
|
|
92
|
-
prCreateCommand,
|
|
93
|
-
prCloseCommand,
|
|
94
|
-
prEditCommand,
|
|
95
|
-
prMergeCommand,
|
|
96
|
-
prReadyCommand,
|
|
97
|
-
prStackCommand,
|
|
98
|
-
prRequestReviewCommand,
|
|
99
|
-
prWaitMergeableCommand,
|
|
100
|
-
prThreadsCommand,
|
|
101
|
-
prCommentsCommand,
|
|
102
|
-
prReviewsCommand,
|
|
103
|
-
prLastHumanReviewerCommand,
|
|
104
|
-
prFeedbackCommand,
|
|
105
|
-
prIssueCommentsCommand,
|
|
106
|
-
prIssueCommentsLatestCommand,
|
|
107
|
-
prCommentCommand,
|
|
108
|
-
prDiscussionSummaryCommand,
|
|
109
|
-
prReplyCommand,
|
|
110
|
-
prResolveCommand,
|
|
111
|
-
prReviewCommand,
|
|
112
|
-
prSubmitReviewCommand,
|
|
113
|
-
prChecksCommand,
|
|
114
|
-
prChecksFailedCommand,
|
|
115
|
-
prWatchCommand,
|
|
116
|
-
prRerunChecksCommand,
|
|
117
|
-
prTriggerChecksCommand,
|
|
118
|
-
prReplyAndResolveCommand,
|
|
119
|
-
prReviewTriageCommand,
|
|
120
|
-
prReviewTriageBatchCommand,
|
|
121
|
-
]),
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
const issueCommand = Command.make("issue", {}).pipe(
|
|
125
|
-
Command.withDescription(
|
|
126
|
-
"Issue operations (list, view, comments, triage, snapshot-batch, create, close, reopen, comment, edit)",
|
|
127
|
-
),
|
|
128
|
-
Command.withSubcommands([
|
|
129
|
-
issueListCommand,
|
|
130
|
-
issueViewCommand,
|
|
131
|
-
issueCommentsCommand,
|
|
132
|
-
issueTriageCommand,
|
|
133
|
-
issueSnapshotBatchCommand,
|
|
134
|
-
issueCreateCommand,
|
|
135
|
-
issueCloseCommand,
|
|
136
|
-
issueReopenCommand,
|
|
137
|
-
issueCommentCommand,
|
|
138
|
-
issueEditCommand,
|
|
139
|
-
]),
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
const branchCommand = Command.make("branch", {}).pipe(
|
|
143
|
-
Command.withDescription("Branch operations (rename)"),
|
|
144
|
-
Command.withSubcommands([branchRenameCommand]),
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
const repoCommand = Command.make("repo", {}).pipe(
|
|
148
|
-
Command.withDescription("Repository operations"),
|
|
149
|
-
Command.withSubcommands([repoInfoCommand, repoListCommand, repoSearchCodeCommand]),
|
|
150
|
-
);
|
|
151
|
-
|
|
152
|
-
const workflowCommand = Command.make("workflow", {}).pipe(
|
|
153
|
-
Command.withDescription(
|
|
154
|
-
"GitHub Actions workflow operations (run, list, view, jobs, logs, job-logs, annotations, rerun, cancel, watch)",
|
|
155
|
-
),
|
|
156
|
-
Command.withSubcommands([
|
|
157
|
-
workflowRunCommand,
|
|
158
|
-
workflowListCommand,
|
|
159
|
-
workflowViewCommand,
|
|
160
|
-
workflowJobsCommand,
|
|
161
|
-
workflowLogsCommand,
|
|
162
|
-
workflowJobLogsCommand,
|
|
163
|
-
workflowAnnotationsCommand,
|
|
164
|
-
workflowRerunCommand,
|
|
165
|
-
workflowCancelCommand,
|
|
166
|
-
workflowWatchCommand,
|
|
167
|
-
]),
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
const releaseCommand = Command.make("release", {}).pipe(
|
|
171
|
-
Command.withDescription("Release operations (create, list, view, edit, delete, status)"),
|
|
172
|
-
Command.withSubcommands([
|
|
173
|
-
releaseCreateCommand,
|
|
174
|
-
releaseListCommand,
|
|
175
|
-
releaseViewCommand,
|
|
176
|
-
releaseEditCommand,
|
|
177
|
-
releaseDeleteCommand,
|
|
178
|
-
releaseStatusCommand,
|
|
179
|
-
]),
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
const gistCommand = Command.make("gist", {}).pipe(
|
|
183
|
-
Command.withDescription("Gist operations (list, view, create, edit, delete)"),
|
|
184
|
-
Command.withSubcommands([
|
|
185
|
-
gistListCommand,
|
|
186
|
-
gistViewCommand,
|
|
187
|
-
gistCreateCommand,
|
|
188
|
-
gistEditCommand,
|
|
189
|
-
gistDeleteCommand,
|
|
190
|
-
]),
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
const commandsCommand = makeSchemaCommand(() => mainCommand);
|
|
194
|
-
|
|
195
|
-
const mainCommand = Command.make("gh-tool", {}).pipe(
|
|
196
|
-
Command.withDescription(
|
|
197
|
-
`GitHub CLI Tool for Coding Agents
|
|
198
|
-
|
|
199
|
-
Wraps the GitHub CLI (gh) with structured output for AI agents.
|
|
200
|
-
Supports PR management, issue management, reviews, CI checks, and repo info.
|
|
201
|
-
|
|
202
|
-
WORKFLOW FOR AI AGENTS:
|
|
203
|
-
1. Use 'pr view' to inspect current PR
|
|
204
|
-
2. Use 'pr discussion-summary' for overview (counts + latest discussion comment)
|
|
205
|
-
3. Use 'pr threads' and 'pr issue-comments-latest --author <username> --body-contains "Review"' for review context
|
|
206
|
-
4. Use 'pr reply', 'pr comment' and 'pr resolve' to handle feedback
|
|
207
|
-
4b. Use 'pr review --event request-changes|approve|comment --confirm' to post a verdict, or 'pr submit-review' for a pending one
|
|
208
|
-
5. Use 'pr checks' to monitor CI status; 'pr trigger-checks --workflow <file.yml>' when zero checks were reported
|
|
209
|
-
6. Use 'pr merge' to merge (dry-run by default)
|
|
210
|
-
7. Use 'issue list' to list open/closed issues
|
|
211
|
-
8. Use 'issue triage --issue N --verbosity full' to inspect one issue in one call
|
|
212
|
-
9. Use 'issue comments --issue N' to read issue discussion comments separately
|
|
213
|
-
10. Use 'issue close --issue N --comment "reason"' to close issues
|
|
214
|
-
11. Use 'issue comment --issue N --body "text"' to comment on issues
|
|
215
|
-
12. Use 'repo info' to get repository metadata
|
|
216
|
-
13. Use 'workflow run' to dispatch workflow_dispatch workflows
|
|
217
|
-
14. Use 'workflow list' to list recent workflow runs
|
|
218
|
-
15. Use 'workflow view --run N' to inspect a specific run with jobs/steps
|
|
219
|
-
16. Use 'workflow logs --run N' to get logs (failed jobs by default)
|
|
220
|
-
17. Use 'workflow job-logs --run N --job "build-web-app"' to get clean parsed logs for a specific job
|
|
221
|
-
18. Use 'workflow annotations --run N' to list CI annotations (errors, warnings, notices)
|
|
222
|
-
19. Use 'workflow watch --run N' to watch until completion
|
|
223
|
-
20. Use 'release status' to inspect latest release + repository context
|
|
224
|
-
21. Use 'release create --tag vX.Y.Z --generate-notes' to publish a release
|
|
225
|
-
22. Use 'release edit/view/list/delete' to maintain existing releases
|
|
226
|
-
23. Use 'branch rename --old-name X --new-name Y --confirm' to rename a branch
|
|
227
|
-
24. Use 'gist list/view' to read gists and 'gist create --files a.ts --desc "..."' to share snippets`,
|
|
228
|
-
),
|
|
229
|
-
Command.withSubcommands([
|
|
230
|
-
prCommand,
|
|
231
|
-
issueCommand,
|
|
232
|
-
repoCommand,
|
|
233
|
-
branchCommand,
|
|
234
|
-
workflowCommand,
|
|
235
|
-
releaseCommand,
|
|
236
|
-
gistCommand,
|
|
237
|
-
commandsCommand,
|
|
238
|
-
]),
|
|
239
|
-
);
|
|
240
|
-
|
|
241
|
-
const cli = Command.run(mainCommand, {
|
|
242
|
-
version: VERSION,
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
const MainLayer = GitHubService.layer.pipe(
|
|
246
|
-
Layer.provideMerge(BunServices.layer),
|
|
247
|
-
Layer.provideMerge(AuditServiceLayer),
|
|
248
|
-
Layer.provideMerge(ConfigServiceLayer),
|
|
249
|
-
);
|
|
250
|
-
|
|
251
|
-
const program = withAudit("gh", cli).pipe(
|
|
252
|
-
Effect.provide(MainLayer),
|
|
253
|
-
Effect.tapCause(renderCauseToStderr),
|
|
254
|
-
);
|
|
255
|
-
|
|
256
|
-
BunRuntime.runMain(program, {
|
|
257
|
-
disableErrorReporting: true,
|
|
258
|
-
});
|
|
4
|
+
if (await checkEffectVersion()) await import("./main");
|
|
5
|
+
else process.exitCode = 1;
|