@blogic-cz/agent-tools 0.2.0 → 0.2.2

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.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "CLI tools for AI coding agent workflows — GitHub, database, Kubernetes, Azure DevOps, logs, and sessions",
5
5
  "keywords": [
6
6
  "agent",
@@ -33,6 +33,19 @@
33
33
  "LICENSE"
34
34
  ],
35
35
  "type": "module",
36
+ "imports": {
37
+ "#shared": "./src/shared/index.ts",
38
+ "#shared/*": "./src/shared/*.ts",
39
+ "#config": "./src/config/index.ts",
40
+ "#config/*": "./src/config/*.ts",
41
+ "#gh/*": "./src/gh-tool/*.ts",
42
+ "#k8s/*": "./src/k8s-tool/*.ts",
43
+ "#db/*": "./src/db-tool/*.ts",
44
+ "#logs/*": "./src/logs-tool/*.ts",
45
+ "#az/*": "./src/az-tool/*.ts",
46
+ "#guard": "./src/credential-guard/index.ts",
47
+ "#session/*": "./src/session-tool/*.ts"
48
+ },
36
49
  "exports": {
37
50
  ".": "./src/index.ts",
38
51
  "./credential-guard": "./src/credential-guard/index.ts",
@@ -3,7 +3,7 @@ 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 { formatAny, formatOption, formatOutput, renderCauseToStderr, VERSION } from "#shared";
7
7
  import {
8
8
  findFailedJobs,
9
9
  getBuildJobSummary,
@@ -12,7 +12,7 @@ import {
12
12
  getBuildTimeline,
13
13
  } from "./build";
14
14
  import { AzService, AzServiceLayer } from "./service";
15
- import { ConfigServiceLayer } from "../config";
15
+ import { ConfigServiceLayer } from "#config";
16
16
 
17
17
  // ---------------------------------------------------------------------------
18
18
  // Common flags shared across build subcommands
@@ -2,12 +2,12 @@ import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
2
2
  import { Effect, Layer, ServiceMap, Stream, Option } from "effect";
3
3
 
4
4
  import type { InvokeParams } from "./types";
5
- import type { AzureConfig } from "../config/types";
5
+ import type { AzureConfig } from "#config/types";
6
6
 
7
7
  import { DIRECT_AZ_COMMANDS, STANDALONE_AZ_COMMANDS } from "./config";
8
8
  import { AzSecurityError, AzCommandError, AzTimeoutError, AzParseError } from "./errors";
9
9
  import { isCommandAllowed, isInvokeAllowed } from "./security";
10
- import { ConfigService, getToolConfig } from "../config";
10
+ import { ConfigService, getToolConfig } from "#config";
11
11
 
12
12
  export class AzService extends ServiceMap.Service<
13
13
  AzService,
@@ -73,11 +73,13 @@ async function findConfigFile(startDirectory: string = process.cwd()): Promise<s
73
73
 
74
74
  while (true) {
75
75
  const json5Path = `${currentDirectory}/agent-tools.json5`;
76
+ // eslint-disable-next-line eslint/no-await-in-loop -- sequential directory walk, each iteration may short-circuit
76
77
  if (await Bun.file(json5Path).exists()) {
77
78
  return json5Path;
78
79
  }
79
80
 
80
81
  const jsonPath = `${currentDirectory}/agent-tools.json`;
82
+ // eslint-disable-next-line eslint/no-await-in-loop -- sequential directory walk, each iteration may short-circuit
81
83
  if (await Bun.file(jsonPath).exists()) {
82
84
  return jsonPath;
83
85
  }
@@ -107,6 +109,7 @@ export async function loadConfig(): Promise<AgentToolsConfig | undefined> {
107
109
  `Invalid agent-tools config at ${configPath}: ${
108
110
  error instanceof Error ? error.message : String(error)
109
111
  }`,
112
+ { cause: error },
110
113
  );
111
114
  }
112
115
  }
@@ -14,7 +14,7 @@
14
14
  * at infrastructure level (K8s RBAC, file permissions, etc.)
15
15
  */
16
16
 
17
- import type { CliToolOverride, CredentialGuardConfig } from "../config/types.ts";
17
+ import type { CliToolOverride, CredentialGuardConfig } from "#config/types.ts";
18
18
 
19
19
  // ============================================================================
20
20
  // TYPES
@@ -108,6 +108,7 @@ const SECRET_PATTERNS = [
108
108
  /(?:secret|token|password|passwd|pwd)[" \t:=]+["']?(?!\$\{|process\.env|z\.|generate|create|read|get|fetch|import|export|const|function|return|Schema)[^\s"']{32,}["']?/i,
109
109
  },
110
110
  {
111
+ // eslint-disable-next-line eslint/no-useless-concat -- intentionally split to avoid credential guard self-detection
111
112
  name: "Priv" + "ate Key",
112
113
  pattern: new RegExp("-----BEGIN.*PRIVATE KEY-----"),
113
114
  },
@@ -1,7 +1,7 @@
1
1
  import { Effect, Layer, ServiceMap } from "effect";
2
2
 
3
- import { ConfigService, getToolConfig } from "../config";
4
- import type { DatabaseConfig } from "../config";
3
+ import { ConfigService, getToolConfig } from "#config";
4
+ import type { DatabaseConfig } from "#config";
5
5
 
6
6
  /**
7
7
  * DbConfigService wraps the resolved DatabaseConfig for the selected profile.
@@ -5,8 +5,8 @@ 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";
9
- import { ConfigService, ConfigServiceLayer, getDefaultEnvironment } from "../config";
8
+ import { formatOption, formatOutput, renderCauseToStderr, VERSION } from "#shared";
9
+ import { ConfigService, ConfigServiceLayer, getDefaultEnvironment } from "#config";
10
10
  import { makeDbConfigLayer } from "./config-service";
11
11
  import { DbConnectionError } from "./errors";
12
12
  import { DbService } from "./service";
@@ -1,4 +1,4 @@
1
- import type { Environment, OutputFormat } from "../shared";
1
+ import type { Environment, OutputFormat } from "#shared";
2
2
  export type { Environment, OutputFormat };
3
3
 
4
4
  export type SchemaMode = "tables" | "columns" | "full" | "relationships";
@@ -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 { renderCauseToStderr, VERSION } from "#shared";
7
7
  import {
8
8
  issueListCommand,
9
9
  issueViewCommand,
@@ -1,7 +1,7 @@
1
1
  import { Command, Flag } from "effect/unstable/cli";
2
2
  import { Effect, Option } from "effect";
3
3
 
4
- import { formatOption, logFormatted } from "../shared";
4
+ import { formatOption, logFormatted } from "#shared";
5
5
  import { GitHubCommandError } from "./errors";
6
6
  import { GitHubService } from "./service";
7
7
 
@@ -1,15 +1,15 @@
1
1
  import { Command, Flag } from "effect/unstable/cli";
2
2
  import { Effect, Option } from "effect";
3
3
 
4
- import type { PRStatusResult } from "../types";
4
+ import type { PRStatusResult } from "#gh/types";
5
5
 
6
- import { formatOption, logFormatted } from "../../shared";
6
+ import { formatOption, logFormatted } from "#shared";
7
7
  import {
8
8
  CI_CHECK_WATCH_TIMEOUT_MS,
9
9
  DEFAULT_DELETE_BRANCH,
10
10
  DEFAULT_MERGE_STRATEGY,
11
11
  MERGE_STRATEGIES,
12
- } from "../config";
12
+ } from "#gh/config";
13
13
 
14
14
  import {
15
15
  createPR,
@@ -1,9 +1,9 @@
1
1
  import { Console, Effect, Option } from "effect";
2
2
 
3
- import type { BranchPRDetail, CheckResult, MergeResult, MergeStrategy, PRInfo } from "../types";
3
+ import type { BranchPRDetail, CheckResult, MergeResult, MergeStrategy, PRInfo } from "#gh/types";
4
4
 
5
- import { GitHubCommandError, GitHubMergeError, GitHubTimeoutError } from "../errors";
6
- import { GitHubService } from "../service";
5
+ import { GitHubCommandError, GitHubMergeError, GitHubTimeoutError } from "#gh/errors";
6
+ import { GitHubService } from "#gh/service";
7
7
 
8
8
  import type { ButStatusJson, PRViewJsonResult } from "./helpers";
9
9
  import { runLocalCommand } from "./helpers";
@@ -155,7 +155,7 @@ export const detectPRStatus = Effect.fn("pr.detectPRStatus")(function* () {
155
155
  { concurrency: "unbounded" },
156
156
  );
157
157
 
158
- const foundPrs = branchResults.filter((r) => r.openPr !== null).map((r) => r.openPr!);
158
+ const foundPrs = branchResults.flatMap((r) => (r.openPr === null ? [] : [r.openPr]));
159
159
 
160
160
  if (foundPrs.length === 0) {
161
161
  const branchDetails: BranchPRDetail[] = branchResults.map((r) => ({
@@ -172,7 +172,7 @@ export const detectPRStatus = Effect.fn("pr.detectPRStatus")(function* () {
172
172
  if (foundPrs.length === 1) {
173
173
  return {
174
174
  mode: "single" as const,
175
- pr: foundPrs[0]!,
175
+ pr: foundPrs[0] as PRInfo,
176
176
  };
177
177
  }
178
178
 
@@ -205,7 +205,11 @@ export const createPR = Effect.fn("pr.createPR")(function* (opts: {
205
205
  "--limit",
206
206
  "1",
207
207
  ])
208
- .pipe(Effect.map((prs) => (prs.length > 0 ? Option.some(prs[0]!) : Option.none())))
208
+ .pipe(
209
+ Effect.map((prs) =>
210
+ prs.length > 0 ? Option.some(prs[0] as PRInfo) : Option.none<PRInfo>(),
211
+ ),
212
+ )
209
213
  : gh
210
214
  .runGhJson<{ number: number; url: string }>(["pr", "view", "--json", "number,url"])
211
215
  .pipe(Effect.option);
@@ -258,7 +262,7 @@ export const createPR = Effect.fn("pr.createPR")(function* (opts: {
258
262
  "1",
259
263
  ]);
260
264
  if (prs.length > 0) {
261
- return prs[0]!;
265
+ return prs[0] as PRInfo;
262
266
  }
263
267
 
264
268
  return yield* Effect.fail(
@@ -1,7 +1,7 @@
1
1
  import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
2
2
  import { Effect, Stream } from "effect";
3
3
 
4
- import { GitHubCommandError } from "../errors";
4
+ import { GitHubCommandError } from "#gh/errors";
5
5
 
6
6
  export type LocalCommandResult = {
7
7
  stdout: string;
@@ -7,10 +7,10 @@ import type {
7
7
  IsoTimestamp,
8
8
  ReviewComment,
9
9
  ReviewThread,
10
- } from "../types";
10
+ } from "#gh/types";
11
11
 
12
- import { GitHubCommandError } from "../errors";
13
- import { GitHubService } from "../service";
12
+ import { GitHubCommandError } from "#gh/errors";
13
+ import { GitHubService } from "#gh/service";
14
14
 
15
15
  import { viewPR } from "./core";
16
16
 
@@ -190,9 +190,12 @@ export const fetchThreads = Effect.fn("pr.fetchThreads")(function* (
190
190
  const threads = response.repository.pullRequest.reviewThreads.nodes;
191
191
 
192
192
  const mapped: ReviewThread[] = threads
193
- .filter((node) => node.comments.nodes.length > 0)
194
193
  .map((node) => {
195
- const comment = node.comments.nodes[0]!;
194
+ const comment = node.comments.nodes[0];
195
+ if (!comment) {
196
+ return null;
197
+ }
198
+
196
199
  return {
197
200
  threadId: node.id,
198
201
  commentId: comment.databaseId,
@@ -201,7 +204,8 @@ export const fetchThreads = Effect.fn("pr.fetchThreads")(function* (
201
204
  body: comment.body,
202
205
  isResolved: node.isResolved,
203
206
  };
204
- });
207
+ })
208
+ .filter((thread): thread is ReviewThread => thread !== null);
205
209
 
206
210
  return unresolvedOnly ? mapped.filter((t) => !t.isResolved) : mapped;
207
211
  });
@@ -1,7 +1,7 @@
1
1
  import { Command, Flag } from "effect/unstable/cli";
2
2
  import { Effect, Option } from "effect";
3
3
 
4
- import { formatOption, logFormatted } from "../shared";
4
+ import { formatOption, logFormatted } from "#shared";
5
5
  import { GitHubCommandError } from "./errors";
6
6
  import { GitHubService } from "./service";
7
7
 
@@ -1,7 +1,7 @@
1
1
  import { Command, Flag } from "effect/unstable/cli";
2
2
  import { Console, Effect, Option } from "effect";
3
3
 
4
- import { formatOption, logFormatted } from "../shared";
4
+ import { formatOption, logFormatted } from "#shared";
5
5
  import { GitHubCommandError, GitHubNotFoundError } from "./errors";
6
6
  import { GitHubService } from "./service";
7
7
 
@@ -5,10 +5,10 @@ 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 { formatOption, formatOutput, renderCauseToStderr, VERSION } from "#shared";
9
9
  import { K8sService, K8sServiceLayer } from "./service";
10
- import { ConfigService, ConfigServiceLayer, getDefaultEnvironment, getToolConfig } from "../config";
11
- import type { K8sConfig } from "../config";
10
+ import { ConfigService, ConfigServiceLayer, getDefaultEnvironment, getToolConfig } from "#config";
11
+ import type { K8sConfig } from "#config";
12
12
  import { K8sContextError } from "./errors";
13
13
 
14
14
  /**
@@ -78,17 +78,17 @@ const runK8sCommand = (command: string, options: CommonK8sCommandOptions) =>
78
78
  const result = yield* k8sService.runKubectl(command, options.dryRun).pipe(
79
79
  Effect.catchTags({
80
80
  K8sContextError: (error) => {
81
- const result: CommandResult = {
81
+ const errorResult: CommandResult = {
82
82
  success: false,
83
83
  error: error.message,
84
84
  hint: `Verify cluster ID "${k8sConfig.clusterId}" matches a context in kubectl config. Run: kubectl config get-contexts`,
85
85
  nextCommand: "kubectl config get-contexts",
86
86
  executionTimeMs: 0,
87
87
  };
88
- return Effect.succeed(result);
88
+ return Effect.succeed(errorResult);
89
89
  },
90
90
  K8sCommandError: (error) => {
91
- const result: CommandResult = {
91
+ const errorResult: CommandResult = {
92
92
  success: false,
93
93
  error: error.message,
94
94
  command: error.command,
@@ -96,10 +96,10 @@ const runK8sCommand = (command: string, options: CommonK8sCommandOptions) =>
96
96
  error.hint ?? "Check command syntax and ensure the target namespace/resource exists.",
97
97
  executionTimeMs: 0,
98
98
  };
99
- return Effect.succeed(result);
99
+ return Effect.succeed(errorResult);
100
100
  },
101
101
  K8sTimeoutError: (error) => {
102
- const result: CommandResult = {
102
+ const errorResult: CommandResult = {
103
103
  success: false,
104
104
  error: error.message,
105
105
  command: error.command,
@@ -108,7 +108,7 @@ const runK8sCommand = (command: string, options: CommonK8sCommandOptions) =>
108
108
  `Command timed out after ${error.timeoutMs}ms. Consider increasing timeoutMs in config or narrowing the query.`,
109
109
  executionTimeMs: error.timeoutMs,
110
110
  };
111
- return Effect.succeed(result);
111
+ return Effect.succeed(errorResult);
112
112
  },
113
113
  }),
114
114
  );
@@ -4,8 +4,8 @@ import { Effect, Layer, Option, Ref, ServiceMap, Stream } from "effect";
4
4
  import type { CommandResult, Environment } from "./types";
5
5
 
6
6
  import { K8sCommandError, K8sContextError, K8sTimeoutError } from "./errors";
7
- import { ConfigService, getToolConfig } from "../config";
8
- import type { K8sConfig } from "../config";
7
+ import { ConfigService, getToolConfig } from "#config";
8
+ import type { K8sConfig } from "#config";
9
9
 
10
10
  export class K8sService extends ServiceMap.Service<
11
11
  K8sService,
@@ -20,8 +20,8 @@ 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";
24
- import { ConfigService, ConfigServiceLayer, getDefaultEnvironment } from "../config";
23
+ import { formatOption, formatOutput, renderCauseToStderr, VERSION } from "#shared";
24
+ import { ConfigService, ConfigServiceLayer, getDefaultEnvironment } from "#config";
25
25
  import { LogsConfigError, LogsNotFoundError, LogsReadError, LogsTimeoutError } from "./errors";
26
26
  import { LogsService, LogsServiceLayer } from "./service";
27
27
 
@@ -3,10 +3,10 @@ import { Effect, Layer, Result, ServiceMap, Stream } from "effect";
3
3
 
4
4
  import type { Environment, LogFile, ReadOptions } from "./types";
5
5
 
6
- import { K8sCommandError } from "../k8s-tool/errors";
7
- import { K8sService, K8sServiceLayer } from "../k8s-tool/service";
8
- import { ConfigService, ConfigServiceLayer, getToolConfig } from "../config/loader";
9
- import type { LogsConfig } from "../config/types";
6
+ import { K8sCommandError } from "#k8s/errors";
7
+ import { K8sService, K8sServiceLayer } from "#k8s/service";
8
+ import { ConfigService, ConfigServiceLayer, getToolConfig } from "#config/loader";
9
+ import type { LogsConfig } from "#config/types";
10
10
  import { LogsNotFoundError, LogsReadError, type LogsError } from "./errors";
11
11
 
12
12
  export const parseLogFiles = (output: string): LogFile[] => {
@@ -1,4 +1,4 @@
1
- import type { Environment, OutputFormat } from "../shared";
1
+ import type { Environment, OutputFormat } from "#shared";
2
2
  export type { Environment, OutputFormat };
3
3
 
4
4
  export type LogFile = {
@@ -2,7 +2,7 @@ import { Effect, Layer, ServiceMap } from "effect";
2
2
  import { homedir } from "node:os";
3
3
  import { join } from "node:path";
4
4
 
5
- import { loadConfig } from "../config/loader";
5
+ import { loadConfig } from "#config/loader";
6
6
 
7
7
  /**
8
8
  * Resolves the OpenCode storage base path from config or default.
@@ -13,7 +13,7 @@ import { Console, Effect, Layer, Result } from "effect";
13
13
 
14
14
  import type { MessageSummary, SessionResult } from "./types";
15
15
 
16
- import { formatOption, formatOutput, VERSION } from "../shared";
16
+ import { formatOption, formatOutput, VERSION } from "#shared";
17
17
  import { ResolvedPaths, ResolvedPathsLayer } from "./config";
18
18
  import { SessionStorageNotFoundError } from "./errors";
19
19
  import { formatDate, SessionService, SessionServiceLayer, truncate } from "./service";
@@ -51,8 +51,10 @@ const readJsonFilesInTree = (parentDir: string): Effect.Effect<FileEntry[], Sess
51
51
  const subPath = `${parentDir}/${subDir}`;
52
52
  let files: string[];
53
53
  try {
54
+ // eslint-disable-next-line eslint/no-await-in-loop -- sequential directory walk, each iteration may short-circuit
54
55
  files = await readdir(subPath);
55
56
  } catch {
57
+ /* ignore unreadable files */
56
58
  continue;
57
59
  }
58
60
 
@@ -63,8 +65,11 @@ const readJsonFilesInTree = (parentDir: string): Effect.Effect<FileEntry[], Sess
63
65
  try {
64
66
  const content = await Bun.file(filePath).text();
65
67
  results.push({ filePath, content });
66
- } catch {}
68
+ } catch {
69
+ /* ignore unreadable files */
70
+ }
67
71
  });
72
+ // eslint-disable-next-line eslint/no-await-in-loop -- sequential directory walk, each iteration may short-circuit
68
73
  await Promise.all(reads);
69
74
  }
70
75
 
@@ -90,7 +95,9 @@ const readJsonFilesFlat = (dir: string): Effect.Effect<FileEntry[], SessionError
90
95
  try {
91
96
  const content = await Bun.file(filePath).text();
92
97
  results.push({ filePath, content });
93
- } catch {}
98
+ } catch {
99
+ /* ignore unreadable files */
100
+ }
94
101
  });
95
102
  await Promise.all(reads);
96
103
 
@@ -191,7 +198,13 @@ export class SessionService extends ServiceMap.Service<
191
198
  }
192
199
  }
193
200
 
194
- return summaries.sort((left, right) => right.created - left.created);
201
+ return (
202
+ summaries as MessageSummary[] & {
203
+ toSorted(
204
+ compareFn: (left: MessageSummary, right: MessageSummary) => number,
205
+ ): MessageSummary[];
206
+ }
207
+ ).toSorted((left, right) => right.created - left.created);
195
208
  }),
196
209
 
197
210
  searchSummaries: (summaries: MessageSummary[], query: string): MessageSummary[] => {
@@ -1,4 +1,4 @@
1
- import type { OutputFormat } from "../shared";
1
+ import type { OutputFormat } from "#shared";
2
2
 
3
3
  export type { OutputFormat };
4
4
 
package/src/shared/bun.ts CHANGED
@@ -51,7 +51,7 @@ export async function runCommand(
51
51
  };
52
52
  }
53
53
 
54
- export async function runShellCommand(
54
+ export function runShellCommand(
55
55
  command: string,
56
56
  options: CommandOptions = {},
57
57
  ): Promise<CommandResult> {
@@ -8,6 +8,7 @@ export { commonArgOptions, parseCommonArgs } from "./cli";
8
8
 
9
9
  export { renderCauseToStderr } from "./error-renderer";
10
10
 
11
+ // eslint-disable-next-line import/no-relative-parent-imports -- package.json lives at project root, outside src/
11
12
  import pkg from "../../package.json" with { type: "json" };
12
13
  export const VERSION = pkg.version;
13
14