@blogic-cz/agent-tools 0.14.50 → 0.14.53

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.50",
3
+ "version": "0.14.53",
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",
@@ -144,11 +144,94 @@ const envsCommand = Command.make(
144
144
  Command.withDescription("List configured database environments and the default (no network)"),
145
145
  );
146
146
 
147
+ const ACTIVITY_SQL = `SELECT pid, usename AS "user", state,
148
+ date_trunc('second', now() - xact_start) AS xact_age,
149
+ date_trunc('second', now() - query_start) AS query_age,
150
+ wait_event_type, wait_event, left(query, 200) AS query
151
+ FROM pg_stat_activity
152
+ WHERE pid <> pg_backend_pid() AND state IS NOT NULL
153
+ ORDER BY xact_start ASC NULLS LAST`;
154
+
155
+ const LOCKS_SQL = `SELECT blocking.pid AS blocking_pid, blocking.usename AS blocking_user,
156
+ left(blocking.query, 150) AS blocking_query,
157
+ blocked.pid AS blocked_pid, blocked.usename AS blocked_user,
158
+ left(blocked.query, 150) AS blocked_query
159
+ FROM pg_stat_activity blocked
160
+ JOIN pg_stat_activity blocking ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
161
+ ORDER BY blocking.pid, blocked.pid`;
162
+
163
+ const GRANTS_SQL = `SELECT grantee, table_schema AS schema, 'table' AS kind, table_name AS object,
164
+ string_agg(privilege_type, ', ' ORDER BY privilege_type) AS privileges
165
+ FROM information_schema.role_table_grants
166
+ WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
167
+ GROUP BY grantee, table_schema, table_name
168
+ UNION ALL
169
+ SELECT grantee, object_schema AS schema, 'sequence' AS kind, object_name AS object,
170
+ string_agg(privilege_type, ', ' ORDER BY privilege_type) AS privileges
171
+ FROM information_schema.role_usage_grants
172
+ WHERE object_type = 'SEQUENCE' AND object_schema NOT IN ('pg_catalog', 'information_schema')
173
+ GROUP BY grantee, object_schema, object_name
174
+ ORDER BY grantee, schema, kind, object`;
175
+
176
+ const makeDiagnosticCommand = (name: string, description: string, sql: string) =>
177
+ Command.make(
178
+ name,
179
+ {
180
+ env: Flag.optional(Flag.string("env")).pipe(
181
+ Flag.withDescription(
182
+ "Target database environment name (e.g. local, test, prod). Falls back to defaultEnvironment in config.",
183
+ ),
184
+ ),
185
+ limit: Flag.optional(Flag.integer("limit")).pipe(
186
+ Flag.withDescription("Max rows to return (default 50). Use 0 for no cap."),
187
+ ),
188
+ format: formatOption,
189
+ profile: Flag.optional(Flag.string("profile")).pipe(
190
+ Flag.withDescription(
191
+ "Database profile name from agent-tools.json5 (if multiple configured)",
192
+ ),
193
+ ),
194
+ },
195
+ ({ env, limit, format }) =>
196
+ Effect.gen(function* () {
197
+ const resolvedEnv = yield* resolveEnv(env);
198
+ const db = yield* DbService;
199
+ const result = yield* db.executeQuery(resolvedEnv, sql, Option.getOrUndefined(limit));
200
+ yield* Console.log(formatOutput(result, format));
201
+ }),
202
+ ).pipe(Command.withDescription(description));
203
+
204
+ const activityCommand = makeDiagnosticCommand(
205
+ "activity",
206
+ "Show live sessions from pg_stat_activity (state, transaction/query age, wait event), longest-running first",
207
+ ACTIVITY_SQL,
208
+ );
209
+
210
+ const locksCommand = makeDiagnosticCommand(
211
+ "locks",
212
+ "Show blocking chains (blocker -> blocked) from pg_blocking_pids + pg_stat_activity",
213
+ LOCKS_SQL,
214
+ );
215
+
216
+ const grantsCommand = makeDiagnosticCommand(
217
+ "grants",
218
+ "Show per-role table/sequence privileges by schema (excludes system schemas)",
219
+ GRANTS_SQL,
220
+ );
221
+
147
222
  const commandsCommand = makeSchemaCommand(() => mainCommand);
148
223
 
149
224
  const mainCommand = Command.make("db-tool", {}).pipe(
150
225
  Command.withDescription("Database Query Tool for Coding Agents"),
151
- Command.withSubcommands([sqlCommand, schemaCommand, envsCommand, commandsCommand]),
226
+ Command.withSubcommands([
227
+ sqlCommand,
228
+ schemaCommand,
229
+ activityCommand,
230
+ locksCommand,
231
+ grantsCommand,
232
+ envsCommand,
233
+ commandsCommand,
234
+ ]),
152
235
  );
153
236
 
154
237
  const cli = Command.run(mainCommand, {
@@ -437,14 +437,16 @@ export const fetchReviews = Effect.fn("pr.fetchReviews")(function* (
437
437
  "Failed to parse response",
438
438
  );
439
439
 
440
- let reviews: PullRequestReview[] = raw.map((review) => ({
441
- id: review.id,
442
- author: review.user?.login ?? "unknown",
443
- state: review.state,
444
- body: review.body ?? "",
445
- submittedAt: (review.submitted_at ?? null) as IsoTimestamp | null,
446
- url: review.html_url,
447
- }));
440
+ let reviews: PullRequestReview[] = raw
441
+ .map((review) => ({
442
+ id: review.id,
443
+ author: review.user?.login ?? "unknown",
444
+ state: review.state,
445
+ body: review.body ?? "",
446
+ submittedAt: (review.submitted_at ?? null) as IsoTimestamp | null,
447
+ url: review.html_url,
448
+ }))
449
+ .filter((review) => !(review.state === "COMMENTED" && review.body.trim() === ""));
448
450
 
449
451
  if (author !== null) {
450
452
  const authorFilter = author.toLowerCase();