@blogic-cz/agent-tools 0.11.0 → 0.12.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @blogic-cz/agent-tools
2
2
 
3
- Safe CLI wrappers for AI coding agents. 8 tools for GitHub, Grafana, databases, Kubernetes, Azure DevOps, logs, OpenCode sessions, and audit history — with JSON5 config and a credential guard that blocks agents from touching secrets.
3
+ Safe CLI wrappers for AI coding agents. 8 tools for GitHub, observability, databases, Kubernetes, Azure DevOps, logs, OpenCode sessions, and audit history — with JSON5 config and a credential guard that blocks agents from touching secrets.
4
4
 
5
5
  ## Why
6
6
 
@@ -32,19 +32,19 @@ bun add -d @blogic-cz/agent-tools
32
32
  Install per-tool skills for only the tools your project uses:
33
33
 
34
34
  ```bash
35
- npx skills add blogic-cz/agent-tools --path skill/gh-tool
36
- npx skills add blogic-cz/agent-tools --path skill/grafana-tool
37
- npx skills add blogic-cz/agent-tools --path skill/db-tool
38
- npx skills add blogic-cz/agent-tools --path skill/k8s-tool
39
- npx skills add blogic-cz/agent-tools --path skill/az-tool
40
- npx skills add blogic-cz/agent-tools --path skill/logs-tool
41
- npx skills add blogic-cz/agent-tools --path skill/session-tool
35
+ npx skills add blogic-cz/agent-tools --path skills/gh-tool
36
+ npx skills add blogic-cz/agent-tools --path skills/observability-tool
37
+ npx skills add blogic-cz/agent-tools --path skills/db-tool
38
+ npx skills add blogic-cz/agent-tools --path skills/k8s-tool
39
+ npx skills add blogic-cz/agent-tools --path skills/az-tool
40
+ npx skills add blogic-cz/agent-tools --path skills/logs-tool
41
+ npx skills add blogic-cz/agent-tools --path skills/session-tool
42
42
  ```
43
43
 
44
44
  Or install the all-in-one skill that covers every tool:
45
45
 
46
46
  ```bash
47
- npx skills add blogic-cz/agent-tools --path skill/agent-tools
47
+ npx skills add blogic-cz/agent-tools --path skills/agent-tools
48
48
  ```
49
49
 
50
50
  **Requirements:** [Bun](https://bun.sh/) >=1.0.0
@@ -62,13 +62,13 @@ Follow these steps to set up agent-tools in the consumer project.
62
62
  - **Non-JavaScript project** (no `package.json`): Skip installation — run tools directly with `bunx`:
63
63
  ```bash
64
64
  bunx gh-tool pr status
65
- bunx grafana-tool health
65
+ bunx observability-tool trace get <trace-id>
66
66
  bunx k8s-tool pods --env test
67
67
  ```
68
68
 
69
69
  **Step 2 — Configure**
70
70
 
71
- Ask the user which tools they need (gh, grafana, db, k8s, az, logs, session, audit). Create `agent-tools.json5` in the project root with **only the selected tools configured**. See [`examples/agent-tools.json5`](./examples/agent-tools.json5) for the full config reference with all options documented.
71
+ Ask the user which tools they need (gh, observability, db, k8s, az, logs, session, audit). Create `agent-tools.json5` in the project root with **only the selected tools configured**. See [`examples/agent-tools.json5`](./examples/agent-tools.json5) for the full config reference with all options documented.
72
72
 
73
73
  Minimal starting config:
74
74
 
@@ -99,32 +99,32 @@ Install **only the per-tool skills the project needs** (recommended), or the all
99
99
 
100
100
  ```bash
101
101
  # Per-tool skills (recommended) — install only what the project uses
102
- npx skills add blogic-cz/agent-tools --path skill/gh-tool
103
- npx skills add blogic-cz/agent-tools --path skill/grafana-tool
104
- npx skills add blogic-cz/agent-tools --path skill/db-tool
105
- npx skills add blogic-cz/agent-tools --path skill/k8s-tool
106
- npx skills add blogic-cz/agent-tools --path skill/az-tool
107
- npx skills add blogic-cz/agent-tools --path skill/logs-tool
108
- npx skills add blogic-cz/agent-tools --path skill/session-tool
102
+ npx skills add blogic-cz/agent-tools --path skills/gh-tool
103
+ npx skills add blogic-cz/agent-tools --path skills/observability-tool
104
+ npx skills add blogic-cz/agent-tools --path skills/db-tool
105
+ npx skills add blogic-cz/agent-tools --path skills/k8s-tool
106
+ npx skills add blogic-cz/agent-tools --path skills/az-tool
107
+ npx skills add blogic-cz/agent-tools --path skills/logs-tool
108
+ npx skills add blogic-cz/agent-tools --path skills/session-tool
109
109
 
110
110
  # All-in-one skill (alternative) — all tools in a single skill
111
- npx skills add blogic-cz/agent-tools --path skill/agent-tools
111
+ npx skills add blogic-cz/agent-tools --path skills/agent-tools
112
112
  ```
113
113
 
114
114
  **Do not run these commands for the user** — they require interactive selection.
115
115
 
116
116
  Available per-tool skills:
117
117
 
118
- | Skill | Install when project uses |
119
- | -------------- | ------------------------------------------ |
120
- | `gh-tool` | GitHub PRs, issues, workflows, CI checks |
121
- | `grafana-tool` | Grafana dashboards, alerts, PromQL, LogQL |
122
- | `db-tool` | SQL queries, schema introspection |
123
- | `k8s-tool` | Kubernetes pods, logs, deployments |
124
- | `az-tool` | Azure DevOps pipelines, builds |
125
- | `logs-tool` | Application log reading (local and remote) |
126
- | `session-tool` | OpenCode session history browsing |
127
- | `agent-tools` | All of the above in a single skill |
118
+ | Skill | Install when project uses |
119
+ | -------------------- | ------------------------------------------- |
120
+ | `gh-tool` | GitHub PRs, issues, workflows, CI checks |
121
+ | `observability-tool` | Tempo traces, Loki logs, Prometheus metrics |
122
+ | `db-tool` | SQL queries, schema introspection |
123
+ | `k8s-tool` | Kubernetes pods, logs, deployments |
124
+ | `az-tool` | Azure DevOps pipelines, builds |
125
+ | `logs-tool` | Application log reading (local and remote) |
126
+ | `session-tool` | OpenCode session history browsing |
127
+ | `agent-tools` | All of the above in a single skill |
128
128
 
129
129
  Then update the project's `AGENTS.md` and/or `CLAUDE.md`:
130
130
 
@@ -185,7 +185,7 @@ bun run agent-tools/example-tool/index.ts ping
185
185
  remotePath: "/app/logs",
186
186
  },
187
187
  },
188
- grafana: {
188
+ observability: {
189
189
  // Profile name (selected via --profile, or used automatically when it's the only one)
190
190
  default: {
191
191
  // Environment name (selected via --env)
@@ -205,7 +205,7 @@ bun run agent-tools/example-tool/index.ts ping
205
205
 
206
206
  ```bash
207
207
  bun gh-tool pr status
208
- bun grafana-tool dashboards list --env local
208
+ bun observability-tool trace get 0b7bdf0dde1c55458364ba5588a8075e --env local
209
209
  bun k8s-tool kubectl --env test --cmd "get pods"
210
210
  bun logs-tool list --env local
211
211
  bun audit-tool list --limit 20
@@ -226,16 +226,16 @@ export default { handleToolExecuteBefore };
226
226
 
227
227
  ## Tools
228
228
 
229
- | Binary | Description |
230
- | -------------- | ---------------------------------------------------------------------------------------------------------------- |
231
- | `gh-tool` | GitHub CLI wrapper — PR management, issues, workflows, composite commands (`review-triage`, `reply-and-resolve`) |
232
- | `grafana-tool` | Grafana API wrapper — health, dashboards, datasources, alerts, PromQL, and LogQL queries |
233
- | `audit-tool` | Audit trail browser — inspect recent tool invocations and purge old entries |
234
- | `db-tool` | Database query tool — SQL execution, schema introspection |
235
- | `k8s-tool` | Kubernetes tool — kubectl wrapper + structured commands (`pods`, `logs`, `describe`, `exec`, `top`) |
236
- | `az-tool` | Azure DevOps tool — pipelines, builds, repos |
237
- | `logs-tool` | Application logs — read local and remote (k8s pod) logs |
238
- | `session-tool` | OpenCode session browser — list, read, search sessions |
229
+ | Binary | Description |
230
+ | -------------------- | ---------------------------------------------------------------------------------------------------------------- |
231
+ | `gh-tool` | GitHub CLI wrapper — PR management, issues, workflows, composite commands (`review-triage`, `reply-and-resolve`) |
232
+ | `observability-tool` | LGTM wrapper — Tempo traces, Loki log correlation, and Prometheus metrics via Grafana |
233
+ | `audit-tool` | Audit trail browser — inspect recent tool invocations and purge old entries |
234
+ | `db-tool` | Database query tool — SQL execution, schema introspection |
235
+ | `k8s-tool` | Kubernetes tool — kubectl wrapper + structured commands (`pods`, `logs`, `describe`, `exec`, `top`) |
236
+ | `az-tool` | Azure DevOps tool — pipelines, builds, repos |
237
+ | `logs-tool` | Application logs — read local and remote (k8s pod) logs |
238
+ | `session-tool` | OpenCode session browser — list, read, search sessions |
239
239
 
240
240
  All tools support `--help` for full usage documentation. Legacy `agent-tools-*` binary names (e.g. `agent-tools-gh`) still work for backwards compatibility.
241
241
 
@@ -247,7 +247,7 @@ Every tool invocation is automatically recorded to a local SQLite database — z
247
247
 
248
248
  ### How it works
249
249
 
250
- Each CLI wrapper (`gh`, `grafana`, `k8s`, `db`, `az`, `logs`, `session`, `audit`) writes a row to `~/.agent-tools/audit.sqlite` on every execution. Logging is fire-and-forget — if the database is unavailable or write fails, the tool continues normally. Audit never blocks or slows down your workflow.
250
+ Each CLI wrapper (`gh`, `observability`, `k8s`, `db`, `az`, `logs`, `session`, `audit`) writes a row to `~/.agent-tools/audit.sqlite` on every execution. Logging is fire-and-forget — if the database is unavailable or write fails, the tool continues normally. Audit never blocks or slows down your workflow.
251
251
 
252
252
  Entries older than `retentionDays` (default: 90) are automatically purged on each write.
253
253
 
@@ -287,16 +287,16 @@ All settings are optional — audit works out of the box with sensible defaults.
287
287
 
288
288
  ### What gets recorded
289
289
 
290
- | Column | Description |
291
- | ----------- | ----------------------------------------------------------------- |
292
- | `ts` | ISO 8601 timestamp |
293
- | `tool` | Tool name (`gh`, `grafana`, `k8s`, `db`, `az`, `logs`, `session`) |
294
- | `project` | Working directory (`process.cwd()`) |
295
- | `args` | Command-line arguments (JSON array) |
296
- | `duration` | Execution time in milliseconds |
297
- | `success` | `1` (success) or `0` (failure) |
298
- | `error` | Error message if failed, `null` otherwise |
299
- | `exit_code` | Process exit code |
290
+ | Column | Description |
291
+ | ----------- | ----------------------------------------------------------------------- |
292
+ | `ts` | ISO 8601 timestamp |
293
+ | `tool` | Tool name (`gh`, `observability`, `k8s`, `db`, `az`, `logs`, `session`) |
294
+ | `project` | Working directory (`process.cwd()`) |
295
+ | `args` | Command-line arguments (JSON array) |
296
+ | `duration` | Execution time in milliseconds |
297
+ | `success` | `1` (success) or `0` (failure) |
298
+ | `error` | Error message if failed, `null` otherwise |
299
+ | `exit_code` | Process exit code |
300
300
 
301
301
  ## Configuration
302
302
 
@@ -350,14 +350,14 @@ See [`examples/agent-tools.json5`](./examples/agent-tools.json5) for a complete
350
350
 
351
351
  Each tool uses its own auth method — no unified token store:
352
352
 
353
- | Tool | Auth Method |
354
- | -------------- | -------------------------------------------------------------------------------------------- |
355
- | `gh-tool` | `gh` CLI session (`gh auth login`) or `GITHUB_TOKEN` env var |
356
- | `grafana-tool` | Grafana URL from config plus optional token from `tokenEnvVar` |
357
- | `k8s-tool` | Existing kubectl context (kubeconfig). Cluster ID from config resolves context automatically |
358
- | `az-tool` | `az` CLI session (`az login`) |
359
- | `db-tool` | Password from env var defined by `passwordEnvVar` in config (e.g. `AGENT_TOOLS_DB_PASSWORD`) |
360
- | `logs-tool` | No auth — reads local files or uses k8s-tool for remote access |
353
+ | Tool | Auth Method |
354
+ | -------------------- | -------------------------------------------------------------------------------------------- |
355
+ | `gh-tool` | `gh` CLI session (`gh auth login`) or `GITHUB_TOKEN` env var |
356
+ | `observability-tool` | Grafana URL from config plus optional token from `tokenEnvVar` |
357
+ | `k8s-tool` | Existing kubectl context (kubeconfig). Cluster ID from config resolves context automatically |
358
+ | `az-tool` | `az` CLI session (`az login`) |
359
+ | `db-tool` | Password from env var defined by `passwordEnvVar` in config (e.g. `AGENT_TOOLS_DB_PASSWORD`) |
360
+ | `logs-tool` | No auth — reads local files or uses k8s-tool for remote access |
361
361
 
362
362
  Secrets are **never** stored in the config file. The `db-tool` config references env var **names** only:
363
363
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blogic-cz/agent-tools",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
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",
@@ -9,8 +9,8 @@
9
9
  "database",
10
10
  "devops",
11
11
  "github",
12
- "grafana",
13
12
  "kubernetes",
13
+ "observability",
14
14
  "tools"
15
15
  ],
16
16
  "homepage": "https://github.com/blogic-cz/agent-tools#readme",
@@ -24,17 +24,17 @@
24
24
  "agent-tools-az": "./src/az-tool/index.ts",
25
25
  "agent-tools-db": "./src/db-tool/index.ts",
26
26
  "agent-tools-gh": "./src/gh-tool/index.ts",
27
- "agent-tools-grafana": "./src/grafana-tool/index.ts",
28
27
  "agent-tools-k8s": "./src/k8s-tool/index.ts",
29
28
  "agent-tools-logs": "./src/logs-tool/index.ts",
29
+ "agent-tools-observability": "./src/observability-tool/index.ts",
30
30
  "agent-tools-session": "./src/session-tool/index.ts",
31
31
  "audit-tool": "./src/audit-tool/index.ts",
32
32
  "az-tool": "./src/az-tool/index.ts",
33
33
  "db-tool": "./src/db-tool/index.ts",
34
34
  "gh-tool": "./src/gh-tool/index.ts",
35
- "grafana-tool": "./src/grafana-tool/index.ts",
36
35
  "k8s-tool": "./src/k8s-tool/index.ts",
37
36
  "logs-tool": "./src/logs-tool/index.ts",
37
+ "observability-tool": "./src/observability-tool/index.ts",
38
38
  "session-tool": "./src/session-tool/index.ts"
39
39
  },
40
40
  "files": [
@@ -75,9 +75,9 @@
75
75
  "types": "./dist/db-tool/*.d.ts",
76
76
  "default": "./src/db-tool/*.ts"
77
77
  },
78
- "#grafana/*": {
79
- "types": "./dist/grafana-tool/*.d.ts",
80
- "default": "./src/grafana-tool/*.ts"
78
+ "#observability/*": {
79
+ "types": "./dist/observability-tool/*.d.ts",
80
+ "default": "./src/observability-tool/*.ts"
81
81
  },
82
82
  "#logs/*": {
83
83
  "types": "./dist/logs-tool/*.d.ts",
@@ -123,7 +123,7 @@
123
123
  "check:ci": "bun check.ts ci",
124
124
  "format": "oxfmt",
125
125
  "format:check": "oxfmt --check",
126
- "grafana-tool": "bun src/grafana-tool/index.ts",
126
+ "observability-tool": "bun src/observability-tool/index.ts",
127
127
  "gh-tool": "bun src/gh-tool/index.ts",
128
128
  "lint": "oxlint -c ./.oxlintrc.json --deny-warnings",
129
129
  "lint:fix": "oxlint -c ./.oxlintrc.json --fix",
@@ -2,7 +2,7 @@
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "$id": "https://raw.githubusercontent.com/blogic-cz/agent-tools/main/schemas/agent-tools.schema.json",
4
4
  "title": "Agent Tools Configuration",
5
- "description": "Root configuration for the agent-tools package. Tool-specific sections (azure, kubernetes, database, grafana, logs) are maps of named profiles keyed by profile name. Tools choose a profile via --profile <name> (default key is 'default', single-entry maps can be auto-selected). session, audit, and credentialGuard are global sections.",
5
+ "description": "Root configuration for the agent-tools package. Tool-specific sections (azure, kubernetes, database, observability, logs) are maps of named profiles keyed by profile name. Tools choose a profile via --profile <name> (default key is 'default', single-entry maps can be auto-selected). session, audit, and credentialGuard are global sections.",
6
6
  "type": "object",
7
7
  "additionalProperties": true,
8
8
  "properties": {
@@ -34,12 +34,12 @@
34
34
  "$ref": "#/definitions/DatabaseConfig"
35
35
  }
36
36
  },
37
- "grafana": {
38
- "description": "Named Grafana profiles as Record<string, GrafanaConfig>.",
37
+ "observability": {
38
+ "description": "Named observability profiles as Record<string, ObservabilityConfig>.",
39
39
  "type": "object",
40
40
  "additionalProperties": {
41
- "description": "Grafana profile configuration containing one or more environments.",
42
- "$ref": "#/definitions/GrafanaConfig"
41
+ "description": "Observability profile configuration containing one or more environments.",
42
+ "$ref": "#/definitions/ObservabilityConfig"
43
43
  }
44
44
  },
45
45
  "logs": {
@@ -200,13 +200,13 @@
200
200
  },
201
201
  "required": ["environments"]
202
202
  },
203
- "GrafanaEnvTarget": {
204
- "description": "Single Grafana environment target.",
203
+ "ObservabilityEnvTarget": {
204
+ "description": "Single observability environment target.",
205
205
  "type": "object",
206
206
  "additionalProperties": false,
207
207
  "properties": {
208
208
  "url": {
209
- "description": "Base Grafana URL.",
209
+ "description": "Base Grafana URL used for observability queries.",
210
210
  "type": "string"
211
211
  },
212
212
  "tokenEnvVar": {
@@ -224,16 +224,16 @@
224
224
  },
225
225
  "required": ["url"]
226
226
  },
227
- "GrafanaConfig": {
228
- "description": "Grafana profile configuration.",
227
+ "ObservabilityConfig": {
228
+ "description": "Observability profile configuration.",
229
229
  "type": "object",
230
230
  "additionalProperties": false,
231
231
  "properties": {
232
232
  "environments": {
233
- "description": "Named Grafana environments as Record<string, GrafanaEnvTarget>.",
233
+ "description": "Named observability environments as Record<string, ObservabilityEnvTarget>.",
234
234
  "type": "object",
235
235
  "additionalProperties": {
236
- "$ref": "#/definitions/GrafanaEnvTarget"
236
+ "$ref": "#/definitions/ObservabilityEnvTarget"
237
237
  }
238
238
  }
239
239
  },
@@ -4,8 +4,8 @@ export type {
4
4
  K8sConfig,
5
5
  DbEnvConfig,
6
6
  DatabaseConfig,
7
- GrafanaConfig,
8
- GrafanaEnvTarget,
7
+ ObservabilityConfig,
8
+ ObservabilityEnvTarget,
9
9
  LogsConfig,
10
10
  AuditConfig,
11
11
  CliToolOverride,
@@ -54,15 +54,15 @@ const LogsConfigSchema = Schema.Struct({
54
54
  remotePath: Schema.String,
55
55
  });
56
56
 
57
- const GrafanaEnvTargetSchema = Schema.Struct({
57
+ const ObservabilityEnvTargetSchema = Schema.Struct({
58
58
  url: Schema.String,
59
59
  tokenEnvVar: Schema.optionalKey(Schema.String),
60
60
  prometheusUid: Schema.optionalKey(Schema.String),
61
61
  lokiUid: Schema.optionalKey(Schema.String),
62
62
  });
63
63
 
64
- const GrafanaConfigSchema = Schema.Struct({
65
- environments: Schema.Record(Schema.String, GrafanaEnvTargetSchema),
64
+ const ObservabilityConfigSchema = Schema.Struct({
65
+ environments: Schema.Record(Schema.String, ObservabilityEnvTargetSchema),
66
66
  });
67
67
 
68
68
  const AuditConfigSchema = Schema.Struct({
@@ -80,7 +80,7 @@ const KNOWN_TOP_LEVEL_KEYS = new Set([
80
80
  "azure",
81
81
  "kubernetes",
82
82
  "database",
83
- "grafana",
83
+ "observability",
84
84
  "logs",
85
85
  "session",
86
86
  "audit",
@@ -94,7 +94,7 @@ const AgentToolsConfigSchema = Schema.Struct({
94
94
  azure: Schema.optionalKey(Schema.Record(Schema.String, AzureConfigSchema)),
95
95
  kubernetes: Schema.optionalKey(Schema.Record(Schema.String, K8sConfigSchema)),
96
96
  database: Schema.optionalKey(Schema.Record(Schema.String, DatabaseConfigSchema)),
97
- grafana: Schema.optionalKey(Schema.Record(Schema.String, GrafanaConfigSchema)),
97
+ observability: Schema.optionalKey(Schema.Record(Schema.String, ObservabilityConfigSchema)),
98
98
  logs: Schema.optionalKey(Schema.Record(Schema.String, LogsConfigSchema)),
99
99
  session: Schema.optionalKey(
100
100
  Schema.Struct({
@@ -190,7 +190,7 @@ export const ConfigServiceLayer = Layer.effect(
190
190
 
191
191
  type ProfiledSection = keyof Pick<
192
192
  AgentToolsConfig,
193
- "azure" | "kubernetes" | "database" | "grafana" | "logs"
193
+ "azure" | "kubernetes" | "database" | "observability" | "logs"
194
194
  >;
195
195
 
196
196
  export function getToolConfig<T>(
@@ -43,17 +43,17 @@ export type LogsConfig = {
43
43
  remotePath: string;
44
44
  };
45
45
 
46
- /** Single Grafana environment connection details */
47
- export type GrafanaEnvTarget = {
46
+ /** Single observability environment connection details */
47
+ export type ObservabilityEnvTarget = {
48
48
  url: string;
49
49
  tokenEnvVar?: string;
50
50
  prometheusUid?: string;
51
51
  lokiUid?: string;
52
52
  };
53
53
 
54
- /** Grafana profile configuration */
55
- export type GrafanaConfig = {
56
- environments: Record<string, GrafanaEnvTarget>;
54
+ /** Observability profile configuration */
55
+ export type ObservabilityConfig = {
56
+ environments: Record<string, ObservabilityEnvTarget>;
57
57
  };
58
58
 
59
59
  export type CliToolOverride = {
@@ -99,8 +99,8 @@ export type AgentToolsConfig = {
99
99
  database?: Record<string, DatabaseConfig>;
100
100
  /** Named logs profiles. e.g. { default: { localDir: "...", remotePath: "..." } } */
101
101
  logs?: Record<string, LogsConfig>;
102
- /** Named Grafana profiles. e.g. { default: { environments: { local: {...}, prod: {...} } } } */
103
- grafana?: Record<string, GrafanaConfig>;
102
+ /** Named observability profiles. e.g. { default: { environments: { local: {...}, prod: {...} } } } */
103
+ observability?: Record<string, ObservabilityConfig>;
104
104
  /** Global session config (not per-profile) */
105
105
  session?: {
106
106
  storagePath: string;
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { AgentToolsConfig, GrafanaConfig, GrafanaEnvTarget } from "./config/index";
1
+ export type { AgentToolsConfig, ObservabilityConfig, ObservabilityEnvTarget } from "./config/index";
2
2
 
3
3
  export {
4
4
  AuditService,
@@ -0,0 +1,8 @@
1
+ import { Schema } from "effect";
2
+
3
+ export class ObservabilityToolError extends Schema.TaggedErrorClass<ObservabilityToolError>()(
4
+ "ObservabilityToolError",
5
+ {
6
+ cause: Schema.Unknown,
7
+ },
8
+ ) {}
@@ -9,32 +9,23 @@ import { ConfigServiceLayer } from "#config";
9
9
  import { AuditServiceLayer, withAudit } from "#shared/audit";
10
10
  import { VERSION } from "#shared";
11
11
 
12
- import { alertsCommand } from "./alerts";
13
- import { dashboardsCommand } from "./dashboards";
14
- import { datasourcesCommand } from "./datasources";
15
- import { healthCommand } from "./health";
16
- import { logsCommand } from "./logs";
17
12
  import { metricsCommand } from "./metrics";
13
+ import { traceCommand } from "./trace";
18
14
 
19
15
  const renderCauseToStderr = (cause: Cause.Cause<unknown>) => Console.error(cause.toString());
20
16
 
21
- const mainCommand = Command.make("grafana-tool", {}).pipe(
22
- Command.withDescription("Grafana queries — dashboards, alerts, Prometheus metrics, Loki logs"),
23
- Command.withSubcommands([
24
- healthCommand,
25
- dashboardsCommand,
26
- alertsCommand,
27
- datasourcesCommand,
28
- metricsCommand,
29
- logsCommand,
30
- ]),
17
+ const mainCommand = Command.make("observability-tool", {}).pipe(
18
+ Command.withDescription(
19
+ "LGTM observability queries — Tempo traces, Loki logs, Prometheus metrics",
20
+ ),
21
+ Command.withSubcommands([traceCommand, metricsCommand]),
31
22
  );
32
23
 
33
24
  const cli = Command.run(mainCommand, { version: VERSION });
34
25
 
35
26
  const MainLayer = Layer.mergeAll(BunServices.layer, ConfigServiceLayer, AuditServiceLayer);
36
27
 
37
- const program = withAudit("grafana", cli).pipe(
28
+ const program = withAudit("observability", cli).pipe(
38
29
  Effect.provide(MainLayer),
39
30
  Effect.tapCause(renderCauseToStderr),
40
31
  );
@@ -0,0 +1,129 @@
1
+ import { Console, Effect } from "effect";
2
+ import { Argument, Command, Flag } from "effect/unstable/cli";
3
+
4
+ import { formatOption, formatOutput } from "#shared";
5
+
6
+ import { ObservabilityToolError } from "./errors";
7
+ import {
8
+ envOption,
9
+ formatObservabilityError,
10
+ observabilityDsQuery,
11
+ profileOption,
12
+ resolveConfig,
13
+ } from "./shared";
14
+
15
+ const queryCommand = Command.make(
16
+ "query",
17
+ {
18
+ promql: Argument.string("promql"),
19
+ format: formatOption,
20
+ env: envOption,
21
+ profile: profileOption,
22
+ start: Flag.string("start").pipe(
23
+ Flag.withDescription("Start time (default: now-1h)"),
24
+ Flag.withDefault("now-1h"),
25
+ ),
26
+ end: Flag.string("end").pipe(
27
+ Flag.withDescription("End time (default: now)"),
28
+ Flag.withDefault("now"),
29
+ ),
30
+ step: Flag.integer("step").pipe(
31
+ Flag.withDescription("Step in seconds (default: 60)"),
32
+ Flag.withDefault(60),
33
+ ),
34
+ },
35
+ ({ promql, format, env, profile, start, end, step }) => {
36
+ const startedAt = Date.now();
37
+
38
+ return Effect.gen(function* () {
39
+ const config = yield* resolveConfig(env, profile);
40
+ const response = yield* observabilityDsQuery(
41
+ config,
42
+ config.prometheusUid,
43
+ "prometheus",
44
+ promql,
45
+ {
46
+ instant: false,
47
+ from: start,
48
+ to: end,
49
+ step,
50
+ },
51
+ );
52
+
53
+ if (response.results.A.error) {
54
+ return yield* new ObservabilityToolError({
55
+ cause: new Error(response.results.A.error),
56
+ });
57
+ }
58
+
59
+ const series = (response.results.A.frames ?? []).flatMap((frame) => {
60
+ const fields = frame.schema.fields;
61
+ const timeFieldIndex = fields.findIndex((field) => field.type === "time");
62
+
63
+ if (timeFieldIndex === -1) {
64
+ return [];
65
+ }
66
+
67
+ const timestamps = (frame.data.values[timeFieldIndex] ?? []) as Array<number | string>;
68
+
69
+ return fields.flatMap((field, fieldIndex) => {
70
+ if (fieldIndex === timeFieldIndex || field.type !== "number") {
71
+ return [];
72
+ }
73
+
74
+ const values = (frame.data.values[fieldIndex] ?? []) as Array<number | string | null>;
75
+
76
+ return {
77
+ labels: field.labels ?? {},
78
+ points: timestamps
79
+ .map((timestamp, index) => ({
80
+ timestamp: Number(timestamp),
81
+ value:
82
+ values[index] === null || values[index] === undefined
83
+ ? ""
84
+ : String(values[index]),
85
+ }))
86
+ .filter((point) => point.value.length > 0),
87
+ };
88
+ });
89
+ });
90
+
91
+ const result = {
92
+ success: true,
93
+ message: `Resolved ${series.length} metric series for PromQL query`,
94
+ data: {
95
+ environment: env,
96
+ grafanaUrl: config.url,
97
+ prometheusDatasourceUid: config.prometheusUid,
98
+ query: promql,
99
+ start,
100
+ end,
101
+ step,
102
+ seriesCount: series.length,
103
+ series,
104
+ },
105
+ executionTimeMs: Date.now() - startedAt,
106
+ };
107
+
108
+ yield* Console.log(formatOutput(result, format));
109
+ }).pipe(
110
+ Effect.catch((error) =>
111
+ Effect.gen(function* () {
112
+ const result = {
113
+ success: false,
114
+ message: "Failed to execute PromQL query",
115
+ error: formatObservabilityError(error),
116
+ hint: "Check PromQL syntax and Grafana/Prometheus connectivity",
117
+ executionTimeMs: Date.now() - startedAt,
118
+ };
119
+ yield* Console.log(formatOutput(result, format));
120
+ }),
121
+ ),
122
+ );
123
+ },
124
+ ).pipe(Command.withDescription("Execute PromQL range query via Grafana"));
125
+
126
+ export const metricsCommand = Command.make("metrics", {}).pipe(
127
+ Command.withDescription("Prometheus metric operations via Grafana"),
128
+ Command.withSubcommands([queryCommand]),
129
+ );