Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/add-postgres-cli.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khaale/postgres-cli": minor
---

Add the read-only `pgc` PostgreSQL explorer for named sessions, progressive schema discovery (including comment-aware search, explicit continuation, and metadata availability), bounded queries, and cross-environment comparison.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,26 @@ The following packages are available on npm under the `@khaale` scope:
|:---|:---|:---|
| `glc` | [`@khaale/gitlab-cli`](https://www.npmjs.com/package/@khaale/gitlab-cli) | GitLab exploration and merge-request review workflows |
| `ktc` | [`@khaale/kaiten-cli`](https://www.npmjs.com/package/@khaale/kaiten-cli) | Kaiten task exploration workflows |
| `pgc` | [`@khaale/postgres-cli`](https://www.npmjs.com/package/@khaale/postgres-cli) | Read-only PostgreSQL exploration and cross-environment comparison |

## Installation

You can install these tools globally using your preferred package manager:

```bash
# Using npm
npm install -g @khaale/gitlab-cli @khaale/kaiten-cli
npm install -g @khaale/gitlab-cli @khaale/kaiten-cli @khaale/postgres-cli

# Using pnpm
pnpm add -g @khaale/gitlab-cli @khaale/kaiten-cli
pnpm add -g @khaale/gitlab-cli @khaale/kaiten-cli @khaale/postgres-cli
```

Alternatively, you can run them directly without installation using `npx`:

```bash
npx @khaale/gitlab-cli --help
npx @khaale/kaiten-cli --help
npx @khaale/postgres-cli --help
```

## Repository Layout
Expand Down Expand Up @@ -57,11 +59,11 @@ Install dependencies from the repository root to configure the pre-commit hook.
pnpm install
```

To create development commands for `glc` and `ktc` under `~/.local/bin/`:
To create development commands for `glc`, `ktc`, and `pgc` under `~/.local/bin/`:

```bash
pnpm dev:install
export PATH="$HOME/.local/bin:$PATH"
```

That gives you direct `glc` and `ktc` commands without typing `node ...`.
That gives you direct `glc`, `ktc`, and `pgc` commands without typing `node ...`.
2 changes: 2 additions & 0 deletions openspec/changes/add-postgres-agent-tool/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-08-24
79 changes: 79 additions & 0 deletions openspec/changes/add-postgres-agent-tool/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
## Context

The repository contains two read-oriented agent CLIs with shared configuration, output, and diagnostic conventions. The new tool must work with PostgreSQL servers rather than an HTTP API, must support multiple environments, and must treat credentials and potentially very large result sets as sensitive. See `proposal.md` and the four capability specs for the motivation and observable contract.

## Goals / Non-Goals

**Goals:**

- Add a standalone workspace package for a PostgreSQL agent CLI, using the repository's existing command, configuration, JSON output, testing, and packaging conventions.
- Make named sessions the only agent-facing connection handle; credentials are resolved inside the process and never accepted as query arguments or emitted in output.
- Establish defense-in-depth read-only behavior for every agent operation.
- Make schema exploration and data comparison bounded, deterministic, and explicit about incomplete results.

**Non-Goals:**

- Supporting migrations, backups, restores, replication administration, or arbitrary database administration.
- Providing a general-purpose SQL console or a write-enabled escape hatch in the first version.
- Synchronizing or modifying data between environments.
- Creating database roles, changing grants, or requiring schema changes in connected databases.

## Decisions

### Use a dedicated `postgres-cli` package with a `pgc` executable

The tool will be a new package at `packages/postgres-cli`, distributed under the short executable name `pgc`. It will follow the existing CLI command conventions and reuse `createCliArgParser` plus other utilities from `@khaale/cli-core` for platform-agnostic configuration paths, common errors, field projection, and output conventions. A dedicated package keeps PostgreSQL dependencies and security-sensitive connection code out of the existing GitLab/Kaiten tools.

An MCP server or database-specific shell was considered, but the repository currently distributes self-contained CLIs and the agent can invoke stable JSON commands directly. An MCP adapter can be added later without changing the capability contracts.

### Store named sessions in a restrictive local configuration

The configuration will contain named session definitions, including host, port, database, user, and a password or secret reference, plus safe defaults such as statement timeout and result limits. The configuration path will use the shared platform-agnostic resolver, be created with restrictive permissions where supported, and never be rendered in full.

The runtime will resolve a session name to an internal connection object. Commands will accept only the session name, not a password or raw credential-bearing connection string. Configuration inspection and errors will return redacted metadata. Environment overrides may change non-secret settings and secret references, but raw secrets will not be included in diagnostic or query output.

### Enforce read-only at both the command and database transaction layers

The query execution path will accept only one bounded statement and will reject known mutating, session-changing, transaction-changing, and multi-statement inputs before opening the query. The actual operation will run inside a PostgreSQL read-only transaction with a local statement timeout and will always roll back/close the transaction after the result is collected.

The statement guard is an early, understandable failure mode; PostgreSQL's transaction-level read-only setting is the final database-side enforcement. This is preferred over relying only on the configured role's grants, because the tool must retain its safety behavior even when a session has write privileges. The tool will not expose a write command or a flag that disables these protections.

### Query catalogs in small, navigable slices

Schema commands will query PostgreSQL catalog views for one level at a time. The default response will be an overview with counts and continuation/narrowing information. Catalog list queries will fetch one sentinel row beyond the requested limit so truncation is explicit. Follow-up requests will select a fully qualified schema, table, view, or column and a bounded detail level. Results will use stable ordering and explicit limits; no command will dump the complete catalog into one response by default.

Schema exploration will also provide a catalog search operation that matches object names, including table, view, routine, and column names, with optional schema/object-type filters. Search results will return compact fully qualified references that can be passed to a detail request, rather than expanding every match inline.

Table detail will include PostgreSQL comments for the selected table and each returned column. Catalog search will match both object names and available comments and will return the comment as a compact description, so business terminology documented in the database can be used to discover technical objects without expanding the full schema.

Table detail will include an availability status of `available`, `inaccessible`, or `not_found`. This prevents an empty catalog slice caused by a missing or unauthorized table from being mistaken for a valid table with no columns or relationships.

Table detail will expose foreign-key relationships in two directions: `outgoing` relationships from the selected table to referenced tables, and `incoming` relationships from tables that reference it. Each relationship will include its constraint name, fully qualified endpoints, and ordered source/target column pairs so composite keys remain unambiguous.

Schema metadata will not be cached as a correctness requirement in the first version. A future cache can be added only with a clear invalidation policy, because QA/UAT schemas can diverge and catalog permissions can change.

### Compare bounded results of two queries in memory using explicit keys

The first comparison mode will run independently supplied `leftQuery` and `rightQuery` against two distinct named sessions. The caller supplies one or more key/primary-key columns; those columns must be present under the same names in both result sets. Non-key columns are matched by name as well, so the caller can use SQL aliases to align different source schemas. Each side is bounded by the same row, byte, and timeout limits; the tool validates compatible result shapes before building indexes and reports left-only, right-only, equal, and changed rows.

Values will be normalized into a stable JSON comparison representation while preserving type information where PostgreSQL values cannot be represented safely as plain JSON. Comparison output will include both non-secret source session names and per-source completeness status. If either side is truncated, timed out, unavailable, or incompatible, the result will be marked incomplete rather than reported as equal.

### Use one stable machine-readable output envelope

Successful commands will emit JSON by default and support compact JSON/field projection consistent with the existing tools. Errors will use the repository's `ok: false` envelope with a stable code and sanitized message/details. Human-readable output, if added, will be a rendering of the same bounded result and will not create a separate behavioral contract.

### Ship a companion agent skill with the CLI

The package will include `skills/postgres-cli/SKILL.md` following the existing `glc`/`ktc` companion-skill pattern. It will instruct an agent to run `pgc --json doctor` first, select a named session instead of handling credentials, start schema exploration with an overview or name search, expand only required objects and relationships, keep queries read-only and bounded, and compare two independently supplied query results using same-named key columns. The skill will document JSON as the canonical format and mention Markdown/CSV only as explicit renderings where supported.

## Risks / Trade-offs

- **[Risk] A configured password remains sensitive at rest in a local file.** → Use restrictive file permissions, avoid command-line arguments and logs, support secret references, redact configuration/error output, and document that local filesystem access remains authoritative.
- **[Risk] SQL functions or unusual PostgreSQL statements can have side effects that a textual guard cannot classify perfectly.** → Reject multi-statement/session-control forms, run every operation in a read-only transaction, and treat the database transaction setting as the final safety boundary.
- **[Risk] Large tables make comparison expensive or misleading.** → Enforce shared row/byte/time limits, require explicit keys or a deterministic identity, include truncation/incompleteness markers, and never claim equality from a partial result.
- **[Risk] Catalog visibility differs between QA and UAT roles.** → Return per-object availability and structured permission errors instead of treating inaccessible metadata as an empty schema.
- **[Risk] A PostgreSQL driver increases package size and packaging complexity.** → Keep the dependency isolated to `postgres-cli`, exercise the self-contained pack check in CI, and avoid adding a shared abstraction until another package needs it.

## Migration Plan

No database migration is required. The rollout adds a new package and a local configuration file; existing GitLab/Kaiten packages and connected databases remain unchanged. Rollback is removal of the new package/configuration or reverting the release; because all first-version operations are read-only, rollback does not require data repair.
38 changes: 38 additions & 0 deletions openspec/changes/add-postgres-agent-tool/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## Why

Агенту нужна безопасная и предсказуемая работа с PostgreSQL в окружениях QA/UAT, но большая схема базы быстро переполняет контекст, а передача connection string или пароля агенту создаёт лишний риск. Сейчас в репозитории нет инструмента, который скрывает секреты, ограничивает операции, раскрывает схему по запросу и помогает сопоставлять данные между двумя базами.

## What Changes

- Добавить отдельный PostgreSQL-инструмент `pgc` для агентской работы с именованными сессиями (`qa`, `uat` и т.п.).
- Хранить параметры подключений в конфигурации инструмента и использовать их внутри процесса, не возвращая пароли, connection strings и другие секреты в вывод агенту.
- Ввести read-only режим по умолчанию с защитой на уровне инструмента и PostgreSQL-сессии; операции, меняющие данные или схему, должны отклоняться.
- Предоставить progressive disclosure для схемы: сначала компактный обзор, затем выборочные схемы, таблицы, колонок, индексов и ограничений.
- Добавить bounded-поиск по названиям таблиц, views, колонок и других доступных объектов схемы.
- Показывать входящие и исходящие связи таблиц по внешним ключам, включая соответствия колонок и составные ключи.
- Позволить выполнять параметризованные read-only SQL-запросы с ограничениями на объём результата и стабильным JSON-выводом.
- Добавить сравнение выбранных данных из двух именованных PostgreSQL-сессий с явным указанием источников и пригодным для агента результатом.
- Добавить диагностику соединения, режима безопасности и доступности объектов без раскрытия секретов.
- Добавить companion skill `pgc`, описывающий безопасный workflow агента: preflight, выбор сессии, progressive schema exploration, read-only queries и сравнение результатов.

## Capabilities

### New Capabilities

- `postgres/sessions`: именованные подключения к PostgreSQL, безопасное разрешение конфигурации, выбор сессии и диагностика без раскрытия секретов.
- `postgres/schema-exploration`: progressive disclosure метаданных PostgreSQL с навигацией от обзора к выбранным объектам.
- `postgres/read-only-queries`: выполнение read-only запросов с защитой от мутаций, ограничением результата и машиночитаемым выводом.
- `postgres/data-comparison`: сравнение результатов или выбранных строк между двумя именованными сессиями.

### Modified Capabilities

Изменений требований существующих capability не планируется.

## Impact

- Новый пакет и CLI-инструмент в монорепозитории, по структуре аналогичный `gitlab-cli` и `kaiten-cli`.
- Новая конфигурационная схема для именованных PostgreSQL-сессий и интеграция с общими правилами разрешения конфигурации и редактирования секретов.
- Новая PostgreSQL-клиентская зависимость, пул/управление соединениями, нормализация типов и безопасное форматирование результатов.
- Новые команды или tool-интерфейс для обзора схемы, запросов, сравнения данных и диагностики.
- Новый агентский skill `skills/postgres-cli/SKILL.md`, синхронизированный с CLI-командами и ограничениями безопасности.
- Изменения не должны требовать миграций в подключаемых базах данных и не должны изменять данные в read-only режиме.
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
## Purpose

Compare read-only PostgreSQL results from two named sessions so agents can investigate differences between environments such as QA and UAT.

## ADDED Requirements

### Requirement: Compare results of two read-only queries

The tool SHALL accept two distinct named sessions, an independently supplied read-only query for each side, and one or more key/primary-key columns used to align rows.

#### Scenario: Compare results of different queries across environments

- **WHEN** the caller provides left and right session names, a read-only query for each side, and key columns present under the same names in both results
- **THEN** the tool executes both queries independently, returns both source identities and a comparison result without exposing either session's credentials

#### Scenario: Compare the same session with itself

- **WHEN** both comparison sides resolve to the same session
- **THEN** the tool rejects the request unless an explicit diagnostic mode allows it, and explains that two distinct sources are required

### Requirement: Report row-level differences deterministically

The tool SHALL use caller-provided key/primary-key columns to align rows by the same-named values and SHALL distinguish matching rows, rows present only on the left, rows present only on the right, and rows whose same-named non-key values differ.

#### Scenario: Rows differ between sessions

- **WHEN** a key identifies rows in both results and one or more non-key values differ
- **THEN** the result identifies the key, changed fields, and left/right values in stable order

#### Scenario: A row exists on only one side

- **WHEN** a keyed row appears in only one result
- **THEN** the result classifies it as left-only or right-only and includes the bounded row representation for that side

### Requirement: Make comparison limits and incompleteness explicit

The tool SHALL apply the same safety limits as read-only queries and SHALL identify when either source was truncated, timed out, unavailable, or otherwise unsuitable for a complete comparison.

#### Scenario: One source is incomplete

- **WHEN** one query is limited, fails, or returns incompatible columns
- **THEN** the tool reports an incomplete comparison with per-source status and does not present the result as a complete equality assertion

### Requirement: Support compatible query shapes

The tool SHALL validate that both query results contain the requested key columns and compatible same-named non-key columns before calculating row differences, and SHALL report incompatible shapes as a structured error.

#### Scenario: Key or column shapes are incompatible

- **WHEN** a requested key is missing on one side or same-named comparison columns cannot be aligned
- **THEN** the tool returns a structured compatibility error describing the mismatch without returning credentials
Loading
Loading