@blogic-cz/agent-tools 0.14.42 → 0.14.43

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.42",
3
+ "version": "0.14.43",
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",
@@ -3,7 +3,13 @@ import { Command, Flag } from "effect/unstable/cli";
3
3
  import { BunRuntime, BunServices } from "@effect/platform-bun";
4
4
  import { Console, Effect, Layer, Option } from "effect";
5
5
 
6
- import { formatOption, formatOutput, renderCauseToStderr, VERSION } from "#shared";
6
+ import {
7
+ makeSchemaCommand,
8
+ formatOption,
9
+ formatOutput,
10
+ renderCauseToStderr,
11
+ VERSION,
12
+ } from "#shared";
7
13
  import { AuditService, AuditServiceLayer, withAudit } from "#shared/audit";
8
14
 
9
15
  type AuditToolResult<T> = {
@@ -81,9 +87,11 @@ const purgeCommand = Command.make(
81
87
  }),
82
88
  ).pipe(Command.withDescription("Delete old audit log entries"));
83
89
 
90
+ const commandsCommand = makeSchemaCommand(() => mainCommand);
91
+
84
92
  const mainCommand = Command.make("audit-tool", {}).pipe(
85
93
  Command.withDescription("Audit log inspection and maintenance for agent-tools"),
86
- Command.withSubcommands([listCommand, purgeCommand]),
94
+ Command.withSubcommands([listCommand, purgeCommand, commandsCommand]),
87
95
  );
88
96
 
89
97
  const cli = Command.run(mainCommand, {
@@ -3,7 +3,14 @@ import { Command, Flag } from "effect/unstable/cli";
3
3
  import { BunRuntime, BunServices } from "@effect/platform-bun";
4
4
  import { Console, Effect, Layer, Option } from "effect";
5
5
 
6
- import { formatAny, formatOption, formatOutput, renderCauseToStderr, VERSION } from "#shared";
6
+ import {
7
+ makeSchemaCommand,
8
+ formatAny,
9
+ formatOption,
10
+ formatOutput,
11
+ renderCauseToStderr,
12
+ VERSION,
13
+ } from "#shared";
7
14
  import { AuditServiceLayer, withAudit } from "#shared/audit";
8
15
  import {
9
16
  findFailedJobs,
@@ -173,6 +180,8 @@ EXAMPLES:
173
180
  // Main command with subcommands
174
181
  // ---------------------------------------------------------------------------
175
182
 
183
+ const commandsCommand = makeSchemaCommand(() => mainCommand);
184
+
176
185
  const mainCommand = Command.make("az-tool", {}).pipe(
177
186
  Command.withDescription(
178
187
  `Azure CLI Tool for Coding Agents (READ-ONLY)
@@ -187,7 +196,7 @@ Typed build subcommands:
187
196
  Raw az wrapper:
188
197
  az-tool cmd --cmd "pipelines list"`,
189
198
  ),
190
- Command.withSubcommands([buildCommand, cmdCommand]),
199
+ Command.withSubcommands([buildCommand, cmdCommand, commandsCommand]),
191
200
  );
192
201
 
193
202
  const cli = Command.run(mainCommand, {
@@ -5,7 +5,13 @@ import { Console, Effect, Layer, Option } from "effect";
5
5
 
6
6
  import type { SchemaMode } from "./types";
7
7
 
8
- import { formatOption, formatOutput, renderCauseToStderr, VERSION } from "#shared";
8
+ import {
9
+ makeSchemaCommand,
10
+ formatOption,
11
+ formatOutput,
12
+ renderCauseToStderr,
13
+ VERSION,
14
+ } from "#shared";
9
15
  import { AuditServiceLayer, withAudit } from "#shared/audit";
10
16
  import { ConfigService, ConfigServiceLayer, getDefaultEnvironment } from "#config";
11
17
  import { DbConfigService, makeDbConfigLayer } from "./config-service";
@@ -133,9 +139,11 @@ const envsCommand = Command.make(
133
139
  Command.withDescription("List configured database environments and the default (no network)"),
134
140
  );
135
141
 
142
+ const commandsCommand = makeSchemaCommand(() => mainCommand);
143
+
136
144
  const mainCommand = Command.make("db-tool", {}).pipe(
137
145
  Command.withDescription("Database Query Tool for Coding Agents"),
138
- Command.withSubcommands([sqlCommand, schemaCommand, envsCommand]),
146
+ Command.withSubcommands([sqlCommand, schemaCommand, envsCommand, commandsCommand]),
139
147
  );
140
148
 
141
149
  const cli = Command.run(mainCommand, {
@@ -145,7 +153,9 @@ const cli = Command.run(mainCommand, {
145
153
  const dbConfigLayer = makeDbConfigLayer(profileArg);
146
154
 
147
155
  const MainLayer = DbService.layer.pipe(
148
- Layer.provide(dbConfigLayer),
156
+ // provideMerge (not provide) so DbConfigService stays in the program context for the `envs` command,
157
+ // which reads it directly rather than going through DbService.
158
+ Layer.provideMerge(dbConfigLayer),
149
159
  Layer.provideMerge(ConfigServiceLayer),
150
160
  Layer.provideMerge(BunServices.layer),
151
161
  Layer.provideMerge(AuditServiceLayer),
@@ -9,7 +9,7 @@ import { resolveEnvTemplate } from "#shared/env-template";
9
9
  import { resolveEnvironmentScopedPrerequisites } from "#shared/prerequisites/config";
10
10
  import { runWithProfilePrerequisites } from "#shared/prerequisites/runtime";
11
11
  import { buildApiProbeArgs } from "#shared/k8s-probe";
12
- import { DbConfigService, DbConfigServiceLayer, TUNNEL_CHECK_INTERVAL_MS } from "./config-service";
12
+ import { DbConfigService, TUNNEL_CHECK_INTERVAL_MS } from "./config-service";
13
13
  import {
14
14
  DbConnectionError,
15
15
  DbMutationBlockedError,
@@ -829,5 +829,3 @@ export class DbService extends Context.Service<
829
829
  ),
830
830
  );
831
831
  }
832
-
833
- export const DbServiceLayer = DbService.layer.pipe(Layer.provide(DbConfigServiceLayer));
@@ -3,7 +3,7 @@ import { Command } from "effect/unstable/cli";
3
3
  import { BunRuntime, BunServices } from "@effect/platform-bun";
4
4
  import { Effect, Layer } from "effect";
5
5
 
6
- import { renderCauseToStderr, VERSION } from "#shared";
6
+ import { makeSchemaCommand, renderCauseToStderr, VERSION } from "#shared";
7
7
  import { AuditServiceLayer, withAudit } from "#shared/audit";
8
8
  import { ConfigServiceLayer } from "#config";
9
9
  import {
@@ -150,6 +150,8 @@ const releaseCommand = Command.make("release", {}).pipe(
150
150
  ]),
151
151
  );
152
152
 
153
+ const commandsCommand = makeSchemaCommand(() => mainCommand);
154
+
153
155
  const mainCommand = Command.make("gh-tool", {}).pipe(
154
156
  Command.withDescription(
155
157
  `GitHub CLI Tool for Coding Agents
@@ -188,6 +190,7 @@ WORKFLOW FOR AI AGENTS:
188
190
  branchCommand,
189
191
  workflowCommand,
190
192
  releaseCommand,
193
+ commandsCommand,
191
194
  ]),
192
195
  );
193
196
 
@@ -876,8 +876,10 @@ export const prReviewTriageBatchCommand = Command.make(
876
876
  withRepo(
877
877
  repo,
878
878
  Effect.gen(function* () {
879
+ const numbers = parsePrNumbers(prs);
880
+ if (numbers.length === 0) return yield* emptyBatchError(prs);
879
881
  const results = yield* Effect.all(
880
- parsePrNumbers(prs).map((prNumber) => fetchReviewTriage(prNumber)),
882
+ numbers.map((prNumber) => fetchReviewTriage(prNumber)),
881
883
  { concurrency: "unbounded" },
882
884
  );
883
885
  yield* logFormatted(results, format);
@@ -5,7 +5,13 @@ import { Console, Effect, Layer, Option } from "effect";
5
5
 
6
6
  import type { CommandResult } from "./types";
7
7
 
8
- import { formatOption, formatOutput, renderCauseToStderr, VERSION } from "#shared";
8
+ import {
9
+ makeSchemaCommand,
10
+ formatOption,
11
+ formatOutput,
12
+ renderCauseToStderr,
13
+ VERSION,
14
+ } from "#shared";
9
15
  import { AuditServiceLayer, withAudit } from "#shared/audit";
10
16
  import { K8sService, K8sServiceLayer } from "./service";
11
17
  import { ConfigService, ConfigServiceLayer, getDefaultEnvironment, getToolConfig } from "#config";
@@ -415,6 +421,8 @@ const topCommand = Command.make(
415
421
  }),
416
422
  ).pipe(Command.withDescription("Show pod CPU/memory usage (kubectl top pod)"));
417
423
 
424
+ const commandsCommand = makeSchemaCommand(() => mainCommand);
425
+
418
426
  const mainCommand = Command.make("k8s-tool", {}).pipe(
419
427
  Command.withDescription("Kubernetes CLI Tool for Coding Agents"),
420
428
  Command.withSubcommands([
@@ -424,6 +432,7 @@ const mainCommand = Command.make("k8s-tool", {}).pipe(
424
432
  describeCommand,
425
433
  execCommand,
426
434
  topCommand,
435
+ commandsCommand,
427
436
  ]),
428
437
  );
429
438
 
@@ -20,7 +20,13 @@ import { Console, Effect, Layer, Option, Result } from "effect";
20
20
 
21
21
  import type { Environment, LogResult, ReadOptions } from "./types";
22
22
 
23
- import { formatOption, formatOutput, renderCauseToStderr, VERSION } from "#shared";
23
+ import {
24
+ makeSchemaCommand,
25
+ formatOption,
26
+ formatOutput,
27
+ renderCauseToStderr,
28
+ VERSION,
29
+ } from "#shared";
24
30
  import { AuditServiceLayer, withAudit } from "#shared/audit";
25
31
  import { ConfigService, ConfigServiceLayer, getDefaultEnvironment } from "#config";
26
32
  import { LogsConfigError, LogsNotFoundError, LogsReadError, LogsTimeoutError } from "./errors";
@@ -214,9 +220,11 @@ const readCommand = Command.make(
214
220
  }),
215
221
  ).pipe(Command.withDescription("Read application logs"));
216
222
 
223
+ const commandsCommand = makeSchemaCommand(() => mainCommand);
224
+
217
225
  const mainCommand = Command.make("logs-tool", {}).pipe(
218
226
  Command.withDescription("Application Logs Tool for Coding Agents"),
219
- Command.withSubcommands([listCommand, readCommand]),
227
+ Command.withSubcommands([listCommand, readCommand, commandsCommand]),
220
228
  );
221
229
 
222
230
  const cli = Command.run(mainCommand, {
@@ -7,7 +7,7 @@ import { Command } from "effect/unstable/cli";
7
7
 
8
8
  import { ConfigServiceLayer } from "#config";
9
9
  import { AuditServiceLayer, withAudit } from "#shared/audit";
10
- import { VERSION } from "#shared";
10
+ import { makeSchemaCommand, VERSION } from "#shared";
11
11
 
12
12
  import { metricsCommand } from "./metrics";
13
13
  import { logsCommand } from "./logs";
@@ -15,11 +15,13 @@ import { traceCommand } from "./trace";
15
15
 
16
16
  const renderCauseToStderr = (cause: Cause.Cause<unknown>) => Console.error(cause.toString());
17
17
 
18
+ const commandsCommand = makeSchemaCommand(() => mainCommand);
19
+
18
20
  const mainCommand = Command.make("observability-tool", {}).pipe(
19
21
  Command.withDescription(
20
22
  "LGTM observability queries — Tempo traces, Loki logs, Prometheus metrics",
21
23
  ),
22
- Command.withSubcommands([traceCommand, metricsCommand, logsCommand]),
24
+ Command.withSubcommands([traceCommand, metricsCommand, logsCommand, commandsCommand]),
23
25
  );
24
26
 
25
27
  const cli = Command.run(mainCommand, { version: VERSION });
@@ -13,7 +13,7 @@ import { Console, Effect, Layer, Result } from "effect";
13
13
 
14
14
  import type { MessageSummary, SessionResult, SessionSource } from "./types";
15
15
 
16
- import { formatOption, formatOutput, VERSION } from "#shared";
16
+ import { makeSchemaCommand, formatOption, formatOutput, VERSION } from "#shared";
17
17
  import { AuditServiceLayer, withAudit } from "#shared/audit";
18
18
  import { ResolvedPaths, ResolvedPathsLayer } from "./config";
19
19
  import { SessionStorageNotFoundError } from "./errors";
@@ -289,9 +289,11 @@ const readCommand = Command.make(
289
289
  }),
290
290
  ).pipe(Command.withDescription("Read all messages from a session"));
291
291
 
292
+ const commandsCommand = makeSchemaCommand(() => mainCommand);
293
+
292
294
  const mainCommand = Command.make("session-tool", {}).pipe(
293
295
  Command.withDescription("OpenCode session history tool"),
294
- Command.withSubcommands([listCommand, readCommand, searchCommand]),
296
+ Command.withSubcommands([listCommand, readCommand, searchCommand, commandsCommand]),
295
297
  );
296
298
 
297
299
  const cli = Command.run(mainCommand, {
@@ -8,6 +8,8 @@ export { commonArgOptions, parseCommonArgs } from "./cli";
8
8
 
9
9
  export { renderCauseToStderr } from "./error-renderer";
10
10
 
11
+ export { dumpCommandSchema, makeSchemaCommand, type CommandSchema } from "./schema-dump";
12
+
11
13
  // eslint-disable-next-line import/no-relative-parent-imports -- package.json lives at project root, outside src/
12
14
  import pkg from "../../package.json" with { type: "json" };
13
15
  export const VERSION = pkg.version;
@@ -0,0 +1,94 @@
1
+ import { Command } from "effect/unstable/cli";
2
+ import { Option } from "effect";
3
+
4
+ import { formatOption, logFormatted } from "./format";
5
+
6
+ /**
7
+ * Machine-readable command-tree dump so an agent can discover the whole CLI surface in ONE call
8
+ * instead of repeated `--help` round-trips (the top remaining discovery friction in the usage audit).
9
+ */
10
+ export type FlagSchema = {
11
+ readonly name: string;
12
+ readonly type?: string;
13
+ readonly choices?: readonly string[];
14
+ readonly description?: string;
15
+ readonly aliases?: readonly string[];
16
+ };
17
+
18
+ export type CommandSchema = {
19
+ readonly name: string;
20
+ readonly description?: string;
21
+ readonly flags: readonly FlagSchema[];
22
+ readonly subcommands: readonly CommandSchema[];
23
+ };
24
+
25
+ // Leaf parameter from effect/unstable/cli; carries the user-facing flag name/description/type.
26
+ type SingleParam = {
27
+ readonly _tag: "Single";
28
+ readonly name: string;
29
+ readonly description: Option.Option<string>;
30
+ readonly aliases?: readonly string[];
31
+ readonly primitiveType?: { readonly _tag?: string; readonly choiceKeys?: readonly string[] };
32
+ };
33
+
34
+ // Minimal view of the (partly internal) Command runtime shape we walk. Public fields: name,
35
+ // description, subcommands; `config.flags` is internal but stable, read defensively.
36
+ type CommandNode = {
37
+ readonly name: string;
38
+ readonly description?: string;
39
+ readonly subcommands?: ReadonlyArray<{ readonly commands?: ReadonlyArray<unknown> }>;
40
+ readonly config?: { readonly flags?: ReadonlyArray<unknown> };
41
+ };
42
+
43
+ const isObject = (value: unknown): value is Record<string, unknown> =>
44
+ typeof value === "object" && value !== null;
45
+
46
+ // Flags wrap as Optional/Map/Transform/Variadic around a leaf Single; descend via `.param`.
47
+ const unwrapToSingle = (param: unknown): SingleParam | undefined => {
48
+ let current: unknown = param;
49
+ for (let depth = 0; depth < 16 && isObject(current); depth++) {
50
+ if (current._tag === "Single" && typeof current.name === "string") {
51
+ return current as unknown as SingleParam;
52
+ }
53
+ current = current.param;
54
+ }
55
+ return undefined;
56
+ };
57
+
58
+ const flagToSchema = (param: unknown): FlagSchema | undefined => {
59
+ const single = unwrapToSingle(param);
60
+ if (!single) return undefined;
61
+ const primitive = single.primitiveType;
62
+ const choices = primitive?.choiceKeys;
63
+ return {
64
+ name: single.name,
65
+ type: primitive?._tag,
66
+ choices: choices && choices.length > 0 ? choices : undefined,
67
+ description: Option.getOrUndefined(single.description),
68
+ aliases: single.aliases && single.aliases.length > 0 ? single.aliases : undefined,
69
+ };
70
+ };
71
+
72
+ export const dumpCommandSchema = (command: unknown): CommandSchema => {
73
+ const node = command as CommandNode;
74
+ const flags = (node.config?.flags ?? [])
75
+ .map(flagToSchema)
76
+ .filter((flag): flag is FlagSchema => flag !== undefined);
77
+ const subcommands = (node.subcommands ?? [])
78
+ .flatMap((group) => group.commands ?? [])
79
+ .map(dumpCommandSchema);
80
+ return { name: node.name, description: node.description, flags, subcommands };
81
+ };
82
+
83
+ /**
84
+ * Builds the `commands` subcommand for a tool. Pass a thunk returning the tool's root command so the
85
+ * dump reflects the fully-assembled tree (the root is defined after its subcommands, incl. this one).
86
+ */
87
+ export const makeSchemaCommand = (getRoot: () => unknown) =>
88
+ Command.make("commands", { format: formatOption }, ({ format }) =>
89
+ logFormatted(dumpCommandSchema(getRoot()), format),
90
+ ).pipe(
91
+ Command.withDescription(
92
+ "Dump the full command tree (names, descriptions, flags, types) as structured output — fetch once instead of repeated --help.",
93
+ ),
94
+ );